2012-11-06 135 views
14

我试图用内在学习代码和下面是做加法打印一个__m128i变量

compiler used: icc

#include<stdio.h> 
#include<emmintrin.h> 
int main() 
{ 
     __m128i a = _mm_set_epi32(1,2,3,4); 
     __m128i b = _mm_set_epi32(1,2,3,4); 
     __m128i c; 
     c = _mm_add_epi32(a,b); 
     printf("%d\n",c[2]); 
     return 0; 
} 

我得到下面的错误代码:

test.c(9): error: expression must have pointer-to-object type 
     printf("%d\n",c[2]); 

如何打印变量c中的值,该变量的类型为__m128i

+0

另请注意,'__m128i'没有关于正在存储的类型的任何信息。它可以是8位整数,16位整数,32位等等。一些编译器支持'.m128i_i32'字段扩展。但这绝对不是标准,也不是GCC。 – Mysticial

+1

与标题相关:[如何使用gcc打印__uint128_t数字?](http://stackoverflow.com/q/11656241/4279) – jfs

+1

请注意,某些编译器对SIMD类型具有内置的printf支持,例如, Apple的gcc,clang等版本都支持'%vld'打印'__m128i'为4 x 32位整数。 –

回答

16

使用此功能可打印出来:

void print128_num(__m128i var) 
{ 
    uint16_t *val = (uint16_t*) &var; 
    printf("Numerical: %i %i %i %i %i %i %i %i \n", 
      val[0], val[1], val[2], val[3], val[4], val[5], 
      val[6], val[7]); 
} 

你打印前拆分成128位16位(或32位)。

这是64位的分裂和印刷的方式,如果您有可用的64位支持:

void print128_num(__m128i var) 
{ 
    int64_t *v64val = (int64_t*) &var; 
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]); 
} 
+0

如果你想要int,用'lld'替换'llx'。 – askmish

+0

它的工作原理。我用uint32_t来打印32位整数。但是输出是相反的。 '2,4,6,8'而不是'8,6,4,2'。 _mm_add_epi32是否以相反的顺序存储值? – arunmoezhi

+0

你读过Endian-ness了吗? – askmish

1
#include<stdio.h> 
#include<emmintrin.h> 
int main() 
{ 
    __m128i a = _mm_set_epi32(1,2,3,4); 
    __m128i b = _mm_set_epi32(1,2,3,4); 
    __m128i c; 

    const int32_t* q; 
    //add a pointer 
    c = _mm_add_epi32(a,b); 

    q = (const int32_t*) &c; 
    printf("%d\n",q[2]); 
    //printf("%d\n",c[2]); 
    return 0; 
} 

试试这个代码。

+3

此代码别名指针。你确定它是合法的吗? –

+0

@NateEldredge:我确定这不是严格合法的(除非你使用'-fno-strict-aliasing'或其他)。我发布了一个安全的答案。 –

4

我知道这个问题被标记为C,但它也是最好的搜索结果,当寻找相同的问题的C++解决方案。

所以,这可能是一个C++实现:

#include <string> 
#include <cstring> 
#include <sstream> 

#if defined(__SSE2__) 
template <typename T> 
std::string __m128i_toString(const __m128i var) { 
    std::stringstream sstr; 
    T values[16/sizeof(T)]; 
    std::memcpy(values,&var,sizeof(values)); //See discussion below 
    if (sizeof(T) == 1) { 
     for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible 
      sstr << (int) values[i] << " "; 
     } 
    } else { 
     for (unsigned int i = 0; i < sizeof(__m128i)/sizeof(T); i++) { //C++11: Range for also possible 
      sstr << values[i] << " "; 
     } 
    } 
    return sstr.str(); 
} 
#endif 

用法:

#include <iostream> 
[..] 
__m128i x 
[..] 
std::cout << __m128i_toString<uint8_t>(x) << std::endl; 
std::cout << __m128i_toString<uint16_t>(x) << std::endl; 
std::cout << __m128i_toString<uint32_t>(x) << std::endl; 
std::cout << __m128i_toString<uint64_t>(x) << std::endl; 

结果:

141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0 
29325 0 0 0 26775 0 0 0 
29325 0 26775 0 
29325 26775 

注:存在一个简单的方法来避免if (size(T)==1),见https://stackoverflow.com/a/28414758/2436175

+0

你应该使用'alignas(16)T值[16/sizeof(T)];'''_mm_storeu_si128((__m128i *)values,var);'其余所有代码都可以正常工作。并且简化了,因为你可以使用范围for像for(T v:values)',我想。 –

+0

@PeterCordes我明白你的观点。我想知道是否可以简单地使用memcpy,这将免除需要对齐缓冲区的必要性。 – Antonio

+0

看到我的答案。如果您没有针对'alignas'的C++ 11或特定于编译器的指令,请使用'storeu'而不是'store'。它可能仍会优化。 (顺便说一下,现代的Windows/Linux已经将堆栈对齐到16B,所以如果实际上存储/重新加载,那么编译器并不花费编译器对齐缓冲区。) –

4
  • 可通过gcc/clang/ICC/MSVC,C和C++进行移植。
  • 所有优化级别全面安全:没有指针别名(不像大多数其他的答案)
  • 打印十六进制为U8,U16,U32或U64元素(based on @AG1's answer
  • 打印内存中的顺序(首先是最不重要的元素,如_mm_setr_epiX)。如果您喜欢按照英特尔手册使用的相同顺序打印,请反转阵列索引,其中最重要的元素位于左侧(如_mm_set_epiX)。相关:Convention for displaying vector registers

使用__m128i*从的int阵列装入因为__m128类型被定义为允许混叠是安全的。 (例如,在海湾合作委员会的标题中,定义包括__attribute__((may_alias))。)

反向不是安全。它可能碰巧在大多数情况下工作,但为什么冒这个险呢?

(uint32_t*) &my_vector违反C和C++别名规则,并且不保证以您期望的方式工作。存储到本地阵列,然后访问它保证是安全的。它甚至可以优化大多数编译器,例如,您可以直接从xmm获取movq/pextrq到整数寄存器,而不是实际的存储/重载。

Source + asm output on the Godbolt compiler explorer:证明它用MSVC编译等等。

#include <immintrin.h> 
#include <stdint.h> 
#include <stdio.h> 

#ifndef __cplusplus 
#include <stdalign.h> // C11 defines _Alignas(). This header defines alignas() 
#endif 

void p128_hex_u8(__m128i in) { 
    alignas(16) uint8_t v[16]; 
    _mm_store_si128((__m128i*)v, in); 
    printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x\n", 
      v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], 
      v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); 
} 

void p128_hex_u16(__m128i in) { 
    alignas(16) uint16_t v[8]; 
    _mm_store_si128((__m128i*)v, in); 
    printf("v8_u16: %x %x %x %x, %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); 
} 

void p128_hex_u32(__m128i in) { 
    alignas(16) uint32_t v[4]; 
    _mm_store_si128((__m128i*)v, in); 
    printf("v4_u32: %x %x %x %x\n", v[0], v[1], v[2], v[3]); 
} 

void p128_hex_u64(__m128i in) { 
    alignas(16) long long v[2]; // uint64_t might give format-string warnings with %llx; it's just long in some ABIs 
    _mm_store_si128((__m128i*)v, in); 
    printf("v2_u64: %llx %llx\n", v[0], v[1]); 
} 

如果需要移植到C99或C++ 03或更早(即没有C11/C++ 11),取出和alignas()使用storeu代替store。或者使用__attribute__((aligned(16)))__declspec(align(16))。 (如果你正在编写带有内在函数的代码,你应该使用最近的编译器版本,较之以前的编译器,包括SSE/AVX内在函数,通常比asm编译器更好,但也许你想用gcc-6.3来-std=gnu++03 C++ 03模式为代码库是没有准备好C++ 11或东西。)从主叫所有4个功能输出


样品上

// source used: 
__m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 
          8, 9, 10, 11, 12, 13, 14, 15, 16); 

// output: 

v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09 
v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d 
v8_u16: 0x201 0x403 0x605 0x807 | 0xa09 0xc0b 0xe0d 0x100f 
v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10 

调整格式字符串如果你想填充前导零来保持一致输出宽度。见printf(3)