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

时间:2010-06-04 15:08:29

标签: c++ alignment intrinsics

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

在我的程序开始时,我创建了一个带有成员的对象:

static __m128 *m_sincos;

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

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));



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

movaps xmm0, m_sincos[t] //crashes
movups xmm0, m_sincos[t] //does not crash

- 变量似乎不正确

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

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

__m128 _sincos = m_sincos[t];
movaps xmm0, _sincos
movaps result, xmm0

2 个答案:

答案 0 :(得分:10)

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

movaps xmm0, m_sincos[t]

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

movaps xmm0, xmmword ptr [t]

该解释尝试将存储在变量t的地址处的128位值复制到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]

<强>旁注:

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

#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;
}

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

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

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

答案 1 :(得分:1)

你应该总是使用instrinsics,甚至只是打开并留下它们,而不是明确地编写它。这是因为__asm不能移植到64位代码。