3

我最近偶然发现了隐式 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)?

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

根据我的阅读(寻址非整数地址和 sse),隐式加载/存储使用对齐的加载/存储,因此内存必须正确对齐。假设它们对于支持 SSE/AVX 内在函数的大多数编译器(GCC/ICC/MSVC/Clang/MinGW,...)工作相同是否公平?拥有这些隐式加载/存储的动机是什么?

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

4

1 回答 1

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);
}
于 2013-11-02T17:13:49.470 回答