隐式SSE / AVX加载/存储和堆栈

时间:2013-11-01 15:41:13

标签: sse avx

我最近偶然发现了隐含的SSE / AVX加载/存储。我认为这些是GCC的一些特殊扩展,但后来意识到它们也适用于MSVC。

__m128 a = *(__m128*)data    // same as __m128 a = _mm_load_ps(data)?
__m128 *b = (__m128*)result; // same as _mm_store_ps(result, a)?

这些隐式加载/存储的正确语法是什么?

根据我的阅读(Addressing a non-integer address, and sse),隐式加载/存储使用对齐的加载/存储,因此必须正确对齐内存。假设它们对支持SSE / AVX内在函数的大多数编译器(GCC / ICC / MSVC / Clang / MinGW,...)的工作方式相同是否公平?这些隐式加载/存储的动机是什么?

我的下一组问题是关于将SSE / AVX寄存器推送到堆栈。 这是如何实现的?如果堆栈不是16字节对齐怎么办?那么它是否使用未对齐的加载/存储?据我所知,堆栈现在通常是16字节对齐但不一定是32字节对齐(至少在64位模式下)。如果一个算法具有高AVX占用率并且需要经常将AVX寄存器推入堆栈,那么将堆栈对齐到32个字节(例如在带有mpreferred-stack-boundary的GCC中)是否有意义可能会提高性能?

1 个答案:

答案 0 :(得分:2)

你在这里做的是将内存重新解释为由__m128变量填充的内存。这是有效的,因为__m128基本上是4个浮点数(4个整数,或2个双精度数,或......)连续写入一个内存。所以你可以把它当作一个浮点数组。唯一的区别是__m128在16个字节上对齐,同时浮动数组保证仅在4个位上对齐。

最好使用reinterpret_cast进行重新解释:

  // sqrt calculation : b = sqrt(a)
const int N = 1000; // N%4 has to be equal 0!
float a[N] __attribute__((aligned(16))); // Input. Force 16 bytes alignment.
float b[N] __attribute__((aligned(16))); // Result.

for(int i=0; i<N; i+=4) {
  __m128 &aVec = reinterpret_cast<__m128&>(a[i]);
  __m128 &bVec = reinterpret_cast<__m128&>(c_simd[i]);
  bVec = _mm_sqrt_ps(aVec);
}