C ++ SSE本质:将结果存储在变量中

时间:2018-10-13 13:51:11

标签: c++ sse simd intrinsics

我很难理解SSE内在函数将SIMD计算结果存储回“正常变量”中的用法。例如,_mm_store_ps内部函数在《英特尔内部函数指南》中描述如下:

  

void _mm_store_ps(float * mem_addr,__ m128 a)

     

存储128位(由4个压缩的单精度(32位)组成   浮点元素)从内存到内存。 mem_addr必须对齐   在16个字节的边界上,否则一般保护例外可能是   生成。

第一个参数是一个指向浮点数的指针,该浮点数的大小为32位。但是描述指出,内部函数会将128位从a复制到目标mem_addr。

  • mem_addr是否需要为4个浮点数的数组?
  • 如何仅访问a中的特定32位元素并将其存储在单个float中?
  • 我在概念上想念什么?
  • 是否有比_mm_store_ps固有的更好的选择?

这是一个简单的结构,其中doSomething()将1加到结构的x / y。缺少的部分是如何将结果存储回x / y中,而只使用32位宽较高的元素2和3,而未使用1和0。

struct vec2 {
   union {
         struct {
            float data[2];
         };
         struct {
            float x, y;
         };
      };

   void doSomething() {
      __m128 v1 = _mm_setr_ps(x, y, 0, 0);
      __m128 v2 = _mm_setr_ps(1, 1, 0, 0);
      __m128 result = _mm_add_ps(v1, v2);
      // ?? How to store results in x,y ??
   }
}

1 个答案:

答案 0 :(得分:0)

这是一个128位加载或存储,所以是的,arg类似于float mem[4]。请记住,在C语言中,将数组传递给函数/内部函数与传递指针相同。

Intel的内在函数有些特殊,因为它们不遵循正常的严格混叠规则,至少对于整数而言。 (例如,_mm_loadu_si128((const __m128i*)some_pointer)即使指向long的指针也不会违反严格混叠。我认为这同样适用于float / double load / store内部函数,因此您可以放心地使用它们来加载/可以存储/存储任何所需的内容。通常,您会使用_mm_load_ps加载单精度FP位模式,但是通常会将它们保留在float类型的C对象中。

  

如何只访问a中的特定32位元素并将其存储在单个float中?

使用向量随机播放,然后使用_mm_cvtss_f32将向量转换为标量。


加载/存储64位

理想情况下,您可以一次将两个向量打包在一起,也可以对一个X值数组和一个Y值数组进行操作,因此,使用一对向量,您将获得4对XY坐标的X和Y值。 (数组结构而不是结构数组)。

但是您可以像这样表达您想要有效地做的事情:

struct vec2 {
    float x,y;
};

void foo(const struct vec2 *in, struct vec2 *out) {
    __m128d tmp = _mm_load_sd( (const double*)in );  //64-bit zero-extending load with MOVSD
    __m128  inv = _mm_castpd_ps(tmp);             // keep the compiler happy
    __m128  result = _mm_add_ps(inv,  _mm_setr_ps(1, 1, 0, 0) );

    _mm_storel_pi( out, result );
}

GCC 8.2 compiles it like this (on Godbolt),对于x86-64 System V,奇怪的是使用movq而不是movsd进行加载。 gcc 6.3使用movsd

foo(vec2 const*, vec2*):
        movq    xmm0, QWORD PTR [rdi]           # 64-bit integer load
        addps   xmm0, XMMWORD PTR .LC0[rip]     # packed 128-bit float add
        movlps  QWORD PTR [rsi], xmm0           # 64-bit store
        ret

对于向量低半部分(2 float或1 double)的64位存储,可以使用_mm_store_sd。或更好的_mm_storel_pimovlps)。不幸的是,它的内在函数需要一个__m64* arg而不是float*,但这只是Intel内在函数的设计怪癖。他们经常需要强制类型转换。

请注意,我使用_mm_load_sd((const double*)&(in->x))而不是_mm_setr来执行64位加载,该加载零扩展到128位向量。您不希望加载movlps,因为它会合并到现有向量中。那将对以前的任何值产生虚假的依赖关系,并且会产生额外的ALU成本。