为什么我的数据似乎没有对齐?

Bre*_*ett 4 c++ alignment intrinsics

我试图弄清楚如何最好地预先计​​算一些sin和余弦值,将它们存储在对齐的块中,然后在以后用于SSE计算:

在我的程序开始时,我使用成员创建一个对象:

static __m128 *m_sincos;
Run Code Online (Sandbox Code Playgroud)

然后我在构造函数中初始化该成员:

m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++)
  m_sincos[t] = _mm_set_ps(cos(t), sin(t), sin(t), cos(t));
Run Code Online (Sandbox Code Playgroud)



当我使用m_sincos时,我遇到了三个问题:
- 数据似乎没有对齐

movaps xmm0, m_sincos[t] //crashes
movups xmm0, m_sincos[t] //does not crash
Run Code Online (Sandbox Code Playgroud)

- 变量似乎不正确

movaps result, xmm0 // returns values that are not what is in m_sincos[t]
//Although, putting a watch on m_sincos[t] displays the correct values
Run Code Online (Sandbox Code Playgroud)

- 让我感到困惑的是,这会使一切正常(但速度太慢):

__m128 _sincos = m_sincos[t];
movaps xmm0, _sincos
movaps result, xmm0
Run Code Online (Sandbox Code Playgroud)

Ore*_*ner 10

m_sincos[t]是一个C表达式.但是,在汇编指令(__asm?)中,它被解释为x86寻址模式,结果完全不同.例如,VS2008 SP1编译:

movaps xmm0, m_sincos[t]
Run Code Online (Sandbox Code Playgroud)

into :(当应用程序在调试模式下崩溃时,请参阅反汇编窗口)

movaps xmm0, xmmword ptr [t]
Run Code Online (Sandbox Code Playgroud)

该解释尝试将存储在变量地址处的128位值复制t到xmm0中. t但是,在可能未对齐的地址处是32位值.执行该指令可能会导致对齐失败,并且会在奇怪的情况下得到错误的结果,其中t地址是对齐的.

您可以通过使用适当的x86寻址模式来解决此问题.这是缓慢但清晰的版本:

__asm mov eax, m_sincos                  ; eax <- m_sincos
__asm mov ebx, dword ptr t
__asm shl ebx, 4                         ; ebx <- t * 16 ; each array element is 16-bytes (128 bit) long
__asm movaps xmm0, xmmword ptr [eax+ebx] ; xmm0 <- m_sincos[t]
Run Code Online (Sandbox Code Playgroud)

边注:

当我把它放在一个完整的程序中时,会发生奇怪的事情:

#include <math.h>
#include <tchar.h>
#include <xmmintrin.h>

int main()
{
    static __m128 *m_sincos;
    int Bins = 4;

    m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
    for (int t=0; t<Bins; t++) {
        m_sincos[t] = _mm_set_ps(cos((float) t), sin((float) t), sin((float) t), cos((float) t));
        __asm movaps xmm0, m_sincos[t];
        __asm mov eax, m_sincos
        __asm mov ebx, t
        __asm shl ebx, 4
        __asm movaps xmm0, [eax+ebx];
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当你运行它时,如果你密切关注寄存器窗口,你可能会注意到一些奇怪的东西.虽然结果是正确的,xmm0是获得正确的值之前,movaps指令被执行.这是怎么发生的?

查看生成的汇编代码,显示_mm_set_ps()将sin/cos结果加载到xmm0,然后将其保存到内存地址m_sincos[t].但价值仍然存在xmm0._mm_set_ps是一种"内在的",而不是一种函数调用; 它不会尝试恢复它在完成后使用的寄存器的值.

如果需要从中学习,可能是在使用SSE内部函数时,始终使用它们,因此编译器可以为您优化.否则,如果您正在使用内联汇编,请始终使用它.