2017-02-05 114 views
2

我已經實現了program使用SSE2分別比較AVX2和SSE2的vpsadbw指令和psadbw。以下代碼是SSE2程序:悲傷教學這種奇怪行爲的原因是什麼?

#define MAX1 4096 
#define MAX2 MAX1 
#define MAX3 MAX1 

#define NUM_LOOP 1000000000 

double pTime = 0, mTime = 5; 

//global data for sequentila matrix operations 
unsigned char a_char[MAX1][MAX2] __attribute__((aligned(16))); 
unsigned char b_char[MAX2][MAX3] __attribute__((aligned(16))); 
unsigned char c_char[MAX1][MAX3] __attribute__((aligned(16))); 
unsigned short int temp[8]; 


int main() 
{ 
    int i, j, w=0, sad=0; 
    struct timespec tStart, tEnd; 
    double tTotal , tBest=10000; 
    __m128i vec1, vec2, vecT, sad_total; 
    sad_total= _mm_setzero_si128(); 

    do{ 
     clock_gettime(CLOCK_MONOTONIC,&tStart); 

     for(i=0; i<MAX1; i++){ 
      for(j=0; j<MAX2; j+=16){ 

       vec1 = _mm_load_si128((__m128i *)&a_char[i][j]); 
       vec2 = _mm_load_si128((__m128i *)&b_char[i][j]); 
       vecT = _mm_sad_epu8(vec1 , vec2); 
       sad_total = _mm_add_epi64(vecT, sad_total); 

       } 
      } 
     _mm_store_si128((__m128i *)&temp[0], sad_total); 
     sad=temp[0]+temp[2]+temp[4]+temp[6];  

     clock_gettime(CLOCK_MONOTONIC,&tEnd); 
     tTotal = (tEnd.tv_sec - tStart.tv_sec); 
     tTotal += (tEnd.tv_nsec - tStart.tv_nsec)/1000000000.0; 
     if(tTotal<tBest) 
      tBest=tTotal; 
     pTime += tTotal; 

    } while(w++ < NUM_LOOP && pTime < mTime); 
    printf(" The best time: %lf sec in %d repetition for %dX result is %d matrix\n",tBest,w, MAX1, sad); 

    return 0; 
} 

我使用gccskylakeLinux mint 當我生成彙編代碼內環包含一些不必要的移動操作爲SSE2如下:

.L26: 
    vmovdqa xmm1, XMMWORD PTR a_char[rcx+rax] 
    vpsadbw xmm1, xmm1, XMMWORD PTR b_char[rcx+rax] 
    add rax, 16 
    vpaddq xmm3, xmm1, XMMWORD PTR [rsp] 
    cmp rax, 4096 
    vmovaps XMMWORD PTR [rsp], xmm3 
    jne .L26 

由於AVX2生成此彙編代碼:

.L26: 
    vmovdqa ymm1, YMMWORD PTR a_char[rcx+rax] 
    vpsadbw ymm1, ymm1, YMMWORD PTR b_char[rcx+rax] 
    add rax, 32 
    vpaddq ymm2, ymm2, ymm1 
    cmp rax, 4096 
    jne .L26 

我不知道那些2 move instruc的原因重大違反了表現。

+0

好的,我不知道它不可訪問。 – Martin

回答

3

原因是這樣的:

_mm_store_si128((__m128i *)&temp[0], sad_total); 

鏘不介意,使漂亮的代碼不管,但是GCC不喜歡它(失敗的啓發吧?)

隨着該更換的東西,不觸發「這應該是在堆棧上所有的時間」 -heuristic,GCC使得更好的代碼,例如:(未測試)

__m128i sad_total = _mm_setzero_si128(); 
    for(i = 0; i < MAX1; i++) { 
     for(j = 0; j < MAX2; j += 16) { 
      __m128i vec1 = _mm_load_si128((__m128i *)&a_char[i][j]); 
      __m128i vec2 = _mm_load_si128((__m128i *)&b_char[i][j]); 
      __m128i vecT = _mm_sad_epu8(vec1 , vec2); 
      sad_total = _mm_add_epi64(sad_total, vecT); 
     } 
    } 
    __m128i hsum = _mm_add_epi64(sad_total, _mm_bsrli_si128(sad_total, 8)); 
    sad = _mm_cvtsi128_si32(hsum); 

內環現在看起來像

.L2: 
    vmovdqa xmm1, XMMWORD PTR a_char[rdx+rax] 
    vpsadbw xmm1, xmm1, XMMWORD PTR b_char[rdx+rax] 
    add  rax, 16 
    vpaddq xmm2, xmm1, xmm2 
    cmp  rax, 4096 
    jne  .L2 
0

您直接繞過編譯器並通過_mm_load_si128告訴它使用movdqa。它正在做你正在做的事情。這裏有什麼問題?我也注意到你沿着16byte邊界對齊,如果我錯了,隨時糾正我(我不確定你的編譯器如何實現屬性),但是你可能會得到填充結果,這樣每個元素將在16字節的邊界上對齊;如果是這樣,這將影響你展開的影響。如果不是的話,請隨時糾正我。

+0

無論如何都必須加載這些東西,所以我假設問題是累加器溢出到堆棧 – harold

+0

對齊屬性_only_對齊_first_字節(即無填充)。也就是說,鏈接器/加載器將保證該變量具有一個地址,例如'(((unsigned long)&x [0] [0])%16)== 0'。 [索引到數組時]保持這種對齊取決於使用情況(即程序代碼)。爲了正確工作,給定'unsigned char a_char [MAX1] [MAX2]','MAX2'必須是16的倍數,當用'a_char [i] [j]'索引時,'for(j = 0; j

+0

Cheers Craig。這裏我關心的是,如果你對結構的每個成員(即__attribute __(aligned(16)))使用它,那麼對結構做一個sizeof()會隨着對齊參數的變化而增加報告的大小現在測試)?不知道它是如何影響靜態聲明的數組 - 雖然我猜這是你的觀點出現的地方。無法測試這一點,但我使用VS的「等效」,並有填充。只是認爲這可能值得引起注意。 – cdcdcd

相關問題