2013-11-01 52 views
2

我最近偶然发现隐式SSE/AVX加载/存储。我认为这些是GCC的一些特殊扩展,但是后来他们意识到他们也在MSVC上工作。Imprets SSE/AVX加载/存储和堆栈

__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中)可能会提高性能?

+1

我用他们很多在宏在那里我可以在任意的指针类型传递。如果它不对齐,你只会得到一个错位错误。编译器应该已经正确地调整堆栈以适应它正在使用的任何SIMD。 – Mysticial

+1

代码或第二行的注释不正确。 '_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

+0

@plasmacel,你是对的。虽然。我个人绝不会使用隐含的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); 
}