我正在试图弄清楚如何最好地预先计算一些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
答案 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位代码。