2010-06-04 80 views
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,我遇到三個問題:
-The數據似乎不對齊

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

-The變量似乎並不正確

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

我只是做了一個sizeof(__ m128),sizeof(m_sincos [t])和sizeof(result),並且它們都返回了「16」 – Brett 2010-06-04 15:30:31

+0

而不是_aligned_malloc,看起來功能完全相同,地址,當我初始化m_sincos時,地址是:0x260e2720,然後當我去使用它時,地址仍然是0x260e2720 ...那有幫助嗎? – Brett 2010-06-04 15:45:15

+1

hmm,試着在每次加載之前放置assert語句來檢查對齊,而不是彙編,你可以嘗試_mm_load_ps內在? – Anycorn 2010-06-04 15:49:35

回答

10

m_sincos[t]是一個C表達式。然而,在彙編指令中(__asm?),它被解釋爲x86尋址模式,結果完全不同。例如,VS2008 SP1編譯:

movaps xmm0, m_sincos[t] 

成:(見拆卸窗口時在調試模式下的應用程序崩潰)

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()負載正弦/餘弦的結果成xmm0,然後將其保存到的m_sincos[t]內存地址。但價值仍然在xmm0_mm_set_ps是一個'內在的',而不是一個函數調用;它不會嘗試恢復它完成後使用的寄存器的值。

如果需要從中獲得教訓,可能是因爲在使用SSE內部函數時,請始終使用它們,以便編譯器可以爲您優化內容。否則,如果你正在使用內聯彙編,那麼也可以使用它。

+0

哇,這可能是我在所有搜索中閱讀的最佳答案,感謝您的明確解釋!所以,如果我想在整個程序集中使用,這是否意味着我必須執行shl指令才能移動到陣列中正確的位置,就像您使用內部函數一樣?非常感謝!! – Brett 2010-06-04 16:07:37

+1

是的,您需要將數組索引乘以16才能獲得正確的偏移量。 x86有許多尋址模式,可以爲您增加索引,避免顯式移位。然而,我不能找到一個乘以16的數字。並不意味着沒有一個,只是我沒有找到它。另一種方法是在每次迭代中將索引增加16。 – 2010-06-04 16:13:09

+0

今天學到了新東西。謝謝 – Anycorn 2010-06-04 16:13:19

1

你應該總是使用instrinsics,甚至只是把它打開並保留它們,而不是明確地編碼它。這是因爲__asm不能移植到64位代碼。

+0

感謝您的建議,當您發佈時,我正在閱讀該內容! – Brett 2010-06-04 17:55:39

相關問題