【问题标题】:Implict SSE/AVX loads/stores and the stack隐式 SSE/AVX 加载/存储和堆栈
【发布时间】:2013-11-12 20:27:00
【问题描述】:

我最近偶然发现了隐式 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 字节(例如,在 GCC 中使用 mpreferred-stack-boundary)以潜在地提高性能是否有意义?

【问题讨论】:

  • 我在可以传入任意指针类型的宏中经常使用它们。如果它没有对齐,你只会得到一个未对齐的错误。编译器应该已经为它使用的任何 SIMD 正确对齐堆栈。
  • 第二行代码或注释不正确。 _mm_store_ps(result, a) 应该等同于 __m128 *result = (__m128*)a_mm_store_ps 的签名是void _mm_store_ps (float* mem_addr, __m128 a),其中mem_addr 必须与16 字节边界对齐。
  • @plasmacel,你是对的。虽然。我个人永远不会使用隐式 SSE/AVX 加载/存储。

标签: sse avx


【解决方案1】:

您在这里所做的是将内存重新解释为由 __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);
}

【讨论】:

    猜你喜欢
    • 2017-12-16
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 2011-06-25
    • 2018-10-01
    • 2022-12-05
    • 2011-02-26
    相关资源
    最近更新 更多