假设我有两个向量,由两个类型为
double
的数组表示,每个数组的大小为 2。我想添加相应的位置。所以假设向量 i0
和 i1
,我想将 i0[0] + i1[0]
和 i0[1] + i1[1]
加在一起。
由于类型是
double
,我需要两个寄存器。诀窍是将 i0[0]
和 i1[0]
以及 i0[1]
和 i1[1]
放在另一个中,然后将寄存器本身添加到其中。
我的问题是,如果我先调用
_mm_load_ps(i0[0])
,然后调用 _mm_load_ps(i1[0])
,是否会将它们分别放入低位和高位 64 位,或者会用第二个 load
替换寄存器?我如何将两个双打放在同一个寄存器中,以便我可以在之后调用 add_ps
?
我想你想要的是这样的:
double i0[2];
double i1[2];
__m128d x1 = _mm_load_pd(i0);
__m128d x2 = _mm_load_pd(i1);
__m128d sum = _mm_add_pd(x1, x2);
// do whatever you want to with "sum" now
当您执行
_mm_load_pd
时,它将第一个双精度值放入寄存器的低 64 位,将第二个双精度值放入寄存器的高 64 位。因此,在上述加载之后,x1
保留两个 double
值 i0[0]
和 i0[1]
(与 x2
类似)。对 _mm_add_pd
的调用将 x1
和 x2
中的相应元素垂直相加,因此相加后,sum
将 i0[0] + i1[0]
保留在其低 64 位中,将 i0[1] + i1[1]
保留在其高 64 位中。
编辑:我应该指出,使用
_mm_load_pd
代替 _mm_load_ps
没有任何好处。正如函数名称所示,pd
变体显式加载两个压缩双精度数,ps
版本加载四个压缩单精度浮点数。由于这些纯粹是逐位内存移动,并且它们都使用 SSE 浮点单元,因此使用 _mm_load_ps
加载 double
数据不会有任何损失。而且,_mm_load_ps
有一个好处:它的指令编码比_mm_load_pd
短一个字节,因此从指令缓存意义上(以及潜在的指令解码)它更有效;我不是所有复杂问题的专家现代 x86 处理器)。上面使用 _mm_load_ps
的代码看起来像:
double i0[2];
double i1[2];
__m128d x1 = (__m128d) _mm_load_ps((float *) i0);
__m128d x2 = (__m128d) _mm_load_ps((float *) i1);
__m128d sum = _mm_add_pd(x1, x2);
// do whatever you want to with "sum" now
强制转换没有隐含任何功能;它只是使编译器将 SSE 寄存器的内容重新解释为保存双精度数而不是浮点数,以便可以将其传递到双精度算术函数
_mm_add_pd
。
_ps
前缀是“打包单”的缩写,意味着它用于单精度浮点,而不是双精度。
相反,您想要
_mm_load_pd()
。该函数采用一个 16 字节对齐的指针,指向两个 double
数组的第一个成员,并加载它们。所以你会像这样使用它:
__m128d v0 = _mm_load_pd(i0);
__m128d v1 = _mm_load_pd(i1);
v0 = _mm_add_pd(v0, v1);