【问题标题】:SSE Loading & AddingSSE 加载和添加
【发布时间】:2012-03-04 12:44:06
【问题描述】:

假设我有两个向量,由两个 double 类型的数组表示,每个数组的大小为 2。我想添加相应的位置。所以假设向量i0i1,我想把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位和高64位,还是会用第二个load替换寄存器?我如何将两个双打放在同一个寄存器中,以便之后我可以调用add_ps

谢谢,

【问题讨论】:

    标签: c x86 sse simd intrinsics


    【解决方案1】:

    我想你想要的是这样的:

    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 时,它会将第一个 double 放入寄存器的低 64 位,第二个放入高 64 位。因此,在上述负载之后,x1 拥有两个 doublei0[0]i0[1]x2 类似)。对_mm_add_pd的调用将x1x2中的对应元素垂直相加,所以相加之后,sum的低64位保持i0[0] + i1[0],高64位保持i0[1] + i1[1]

    编辑:我应该指出,使用_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

    【讨论】:

    • 您当然可以使用_mm_load_ps,但您可能会冒着降低假设未来处理器性能的风险,该处理器的设计方式是单精度和双精度浮点之间存在域绕过惩罚操作。我知道没有关于这种处理器的计划,但这并不是说永远不会实现。这就是为什么有不同的加载操作。诚然,这是一种遥远的可能性,但为什么要冒险呢?
    • 我同意未来处理器存在性能下降的风险。我建议考虑(即衡量)通过在特定于应用程序的基础上使用 MOVPS 而不是 MOVPD 获得的任何性能优势。如果今天使用它有好处,并且没有迹象表明即将出现的架构会因此而受到惩罚,我会这样做。像这样的负载可以很容易地串联抽象,以便将来自动切换到不同的实现。
    • IMO 你不应该在内部源代码中搞乱_mm_castps_pd:使用更快的等效指令是我们首先拥有编译器的原因。对于编译器来说,发出 movupdmovapd 而不是 movups / movaps 是一个错过的优化。 (或movsd 存储(不加载)而不是movlps)。但在实践中,我们的编译器无法进行这些优化。幸运的是(?)有了 AVX,这终于消失了;甚至 SSE1 指令都带有 2 字节 VEX 前缀,因此 SSE1 到 SSE3 指令的长度都相同(立即数或 xmm8..15 除外)
    【解决方案2】:

    _ps 前缀是“packed single”的缩写,表示它用于单精度浮点,而不是双精度。

    相反,您需要_mm_load_pd()。这个函数接受一个 16 字节对齐的指针,指向两个 doubles 数组的第一个成员,并加载它们。所以你会像这样使用它:

    __m128d v0 = _mm_load_pd(i0);
    __m128d v1 = _mm_load_pd(i1);
    
    v0 = _mm_add_pd(v0, v1);
    

    【讨论】:

    • _mm_load_ps 实际上可以与双精度值一起使用(这样做有好处);看我的回答。
    猜你喜欢
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    相关资源
    最近更新 更多