2012-10-19 17 views
3

我有一個簡單的問題。例如(+ 5,+ 10,-1,-5)具有開始的uint_32值(比如125)和要添加的操作數__m128i。我想盡可能快地得到的是一個向量(125 + 5,125 + 5 + 10,125 + 5 + 10-1,125 + 5 + 10-1-5),即累加操作數的值到初始值。到目前爲止,我能想到的唯一解決方案是增加4個__m128i變量。例如,他們將上證所累計求和

/* pseudoSSE code... */ 
__m128i src =  (125,125,125,125) 
__m128i operands =(5,10,-1,-5) 

/* Here I omit the partitioning of operands into add1,..add4 for brevity */ 

__m128i add1 = (+05,+05,+05,+05) 
__m128i add2 = (+00,+10,+10,+10) 
__m128i add3 = (+00,+00,-01,-01) 
__m128i add4 = (+00,+00,+00,-05) 
__m128i res1 = _mm_add_epu32(add1, add2) 
__m128i res2 = _mm_add_epu32(add3, add4) 
__m128i res3 = _mm_add_epu32(res1, add2) 
__m128i res = _mm_add_epu32(res3, src ) 

像這樣,我得到我想要的。對於這個解決方案,我將需要設置所有的add_變量,然後執行4個添加。我真正要問的是這是否可以更快地完成。可以通過一些不同的算法,也可以使用我還不知道的一些專門的SSE函數(如_mm_cumulative_sum())。非常感謝。

+0

你怎麼可能來,使用SSE將是緩慢的做了結論???你測試過了嗎? –

+1

我不明白。你說你有一個32位的值,但你顯示有四個8位值?這5行「代碼」應該是什麼意思*?爲什麼你必須加載值4次? – jalf

+0

@Tony獅子:那不是我的觀點! Ofc SSE將是最快的解決方案。我的意思是 - 沒有更好的算法(在SSE框架中)比做這4個增加? –

回答

1

謝謝大家的幫助。爲了弄清哪個版本是最快的,我寫了一個測試應用程序。

1/nonSSE版本可以完成所有功能,就像您期望的那樣。

int iRep; 
int iCycle; 
int iVal = 25; 
int a1, a2, a3, a4; 
int dst1 [4]; 
for (iCycle = 0; iCycle < CYCLE_COUNT; iCycle++) 
    for (iRep = 0; iRep < REP_COUNT; iRep++) 
     { 
      a1 = a2 = a3 = a4 = iRep; 
      dst1[0] = iVal + a1; 
      dst1[1] = dst1[0] + a2; 
      dst1[2] = dst1[1] + a3; 
      dst1[3] = dst1[2] + a4; 
     } 

2/SSE-4加法做什麼,我提議,即

__m128i _a1, _a2, _a3, _a4; 
__m128i _res1, _res2, _res3; 
__m128i _val; 
__m128i _res; 

for (iCycle = 0; iCycle < CYCLE_COUNT; iCycle++) 
    for (iRep = 0; iRep < REP_COUNT; iRep++){ 
     a1 = a2 = a3 = a4 = iRep; 

     _val = _mm_set1_epi32(iVal); 
     _a1 = _mm_set_epi32 (a1, a1, a1, a1); 
     _a2 = _mm_set_epi32 (a2, a2, a2, 0 ); 
     _a3 = _mm_set_epi32 (a3, a3, 0 , 0 ); 
     _a4 = _mm_set_epi32 (a4, 0 , 0 , 0 ); 

     _res1 = _mm_add_epi32(_a1, _a2 ); 
     _res2 = _mm_add_epi32(_a3, _a4 ); 
     _res3 = _mm_add_epi32(_val, _res1 ); 
     _res = _mm_add_epi32(_res3, _res2); 
    } 

3/SSE-3加法做什麼葉夫根建議,即

__m128i shift1, shift2, operands ; 
for (iCycle = 0; iCycle < CYCLE_COUNT; iCycle++) 
    for (iRep = 0; iRep < REP_COUNT; iRep++){ 
     a1 = a2 = a3 = a4 = iRep; 

     _val = _mm_set1_epi32(iVal); 
     operands = _mm_set_epi32(a1,a2,a3,a4); 

     shift1 = _mm_add_epi32(operands,    
       _mm_and_si128(_mm_shuffle_epi32(operands, 0xF9), _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF) )); 
     shift2 = _mm_add_epi32(shift1, 
       _mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),  _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)   )); 
     _res = _mm_add_epi32(_val, shift2); 
     } 

的結果

#define REP_COUNT 100000 
#define CYCLE_COUNT 100000 

non-SSE  -> 6.118s 
    SSE-4additions -> 20.775s 
    SSE-3additions -> 14.873s 

相當令人驚訝的...

5

您可以添加一些更多的並行性,並使用3次加法,而不是4:

const __m128i src = _mm_set1_epi32(125); 
const __m128i operands = _mm_set_epi32(5,10,-1,-5); 

const __m128i shift1 = 
    _mm_add_epi32(operands, 
    _mm_and_si128(_mm_shuffle_epi32(operands, 0xF9), 
        _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF))); 

const __m128i shift2 = 
    _mm_add_epi32(shift1, 
    _mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE), 
        _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF))); 

const __m128i res = _mm_add_epi32(src, shift2); 

這裏SSE2指令集使用。使用更新的指令集,您可以使用_mm_shuffle_epi8等單個指令替換_mm_and_si128/_mm_shuffle_epi32。

累積之和與2個加法計算,如下所示:

a b c d 
+  a b c 
    ------------------ 
    a a+b b+c c+d 
+   a a+b 
    ------------------ 
    a a+b a+b+c a+b+c+d 

SSE不爲這樣的任務合身。它的性能僅適用於「垂直」操作,但它需要大量額外的「水平」操作工作,這裏需要。

+0

葉夫根尼,謝謝!我寫了一個應用程序試圖分析哪個版本運行速度最快,但MSVC 2010似乎刪除了我所有的代碼(「優化」)。我想發佈結果,但爲此我需要首先問:關閉優化時,每個版本的結果運行時是否告訴我效率?我的意思是,當我轉向優化時,它不會損害SSE功能的效率嗎?因爲優化關閉,nonSSE運行速度比SSE快4倍。但是,我不知道如何告訴編譯器不要刪除我的「生產性」代碼(重複1M次)。非常感謝。 –

+1

@DanBencik:如果您關閉優化,它應該會損害SSE和非SSE功能的效率。因此,在沒有優化的情況下測試性能並不是一個好主意。爲了告訴編譯器不要刪除「非生產性」代碼,你可以將一些計算結果累加到一個變量中,然後將其打印到控制檯,或者將其發送給其他外部函數,或者寫入volatile變量。 –