为什么访问单个SIMD元素的速度这么慢

时间:2018-08-30 01:49:27

标签: c sse simd

我正在学习C ++中的SIMD内在函数,我有点困惑。假设我有一个__m128,并且我想使用__m128.m128_f32 [0]访问它的第一个元素(我知道并非所有编译器都实现了此功能),为什么这样做很慢。难道不是像其他存储器一样读取存储器吗?我读过其他一些页面,其中提到了诸如Load-Hit-Store之类的内容,但是在我的问题范围内我并没有真正理解它。我知道这样做是不明智的,而且我不打算这样做,但是我对实际上是什么原因导致它如此缓慢感到好奇。

1 个答案:

答案 0 :(得分:3)

SIMD向量变量通常在XMM寄存器中,而不是内存中。向量存储/标量重载是编译器实现读取向量整数元素的一种策略,但绝对不是唯一的策略。通常不是一个好选择。

此建议的重点是,如果要水平和,请使用shuffle / add内在函数来编写它,而不是访问元素并使编译器生成的asm可能比从精心挑选的shuffle中获得的更糟糕。关于C的实现,请参见Fastest way to do horizontal float vector sum on x86,其中包含编译器生成的asm。


通过内存写入向量的元素会更糟,因为向量存储/标量存储重叠/向量重载会导致存储转发停顿。但是,相反,编译器不是那么笨拙,可以使用movd xmm0, eax并使用向量重排将新元素合并为向量。

您读取__m128.m128_f32[0]的特定示例不是一个好例子:它实际上是免费的,因为标量float通常保存在XMM寄存器的低位元素中(除非您要编译32位标量为x87浮点数的传统代码)。因此,XMM寄存器中__m128向量的低位元素已经 是标量浮点数,编译器可以将其与addss指令一起使用。调用约定在XMM寄存器中传递float,并且不需要将高位元素清零,因此在那里没有额外的成本。


在x86上,它并不是灾难性的昂贵,但是您绝对希望避免在内部循环中使用它。对于float,一个好的编译器会将其转换为shuffle,您可以使用最终完成float _mm_cvtss_f32 (__m128 a)的内部函数编写自己(如上所述,编译为零指令)。

对于整数,希望使用SSE4.1获得pextrd eax, xmm0, 3或其他任何东西(或者低价元素movd eax, xmm0更便宜)。


在ARM上,整数寄存器和向量寄存器之间的传输要比x86上的昂贵得多。至少要有更高的延迟,如果吞吐量不错的话。在某些ARM CPU上,CPU的整数部分和向量部分根本不紧密耦合,并且当一侧必须等待另一侧的结果时会出现停顿。 (我想我读过最近的ARM,例如支持AArch64的CPU,通常具有较低的int <-> SIMD延迟。)

(您没有标记x86或SSE,但您确实提到了MSVC的__m128,所以我主要回答x86。