4

我试图弄清楚如何最好地预先计​​算一些正弦和余弦值,将它们存储在对齐的块中,然后稍后将它们用于 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
4

2 回答 2

10

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

movaps xmm0, m_sincos[t]

into:(在调试模式下应用崩溃时查看反汇编窗口)

movaps xmm0, xmmword ptr [t]

该解释试图将存储在变量地址的 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]

边注:

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

#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 内部函数时,请始终使用它们,以便编译器可以为您优化。否则,如果您使用的是内联汇编,也请始终使用它。

于 2010-06-04T15:58:29.323 回答
1

您应该始终使用内在函数,甚至只是将其打开并保留它们,而不是显式地对其进行编码。这是因为 __asm 不能移植到 64 位代码。

于 2010-06-04T16:55:07.543 回答