2017-02-15 68 views
2

以下循環將整數矩陣轉置爲另一個整數矩陣。當我編譯有趣的時候,它會生成movaps指令將結果存儲到輸出矩陣中。爲什麼gcc這樣做?爲什麼這個SSE2程序(整數)產生movaps(float)?

數據:

int __attribute__((aligned(16))) t[N][M] 
    , __attribute__((aligned(16))) c_tra[N][M]; 

循環:

for(i=0; i<N; i+=4){ 
    for(j=0; j<M; j+=4){ 

     row0 = _mm_load_si128((__m128i *)&t[i][j]); 
     row1 = _mm_load_si128((__m128i *)&t[i+1][j]); 
     row2 = _mm_load_si128((__m128i *)&t[i+2][j]); 
     row3 = _mm_load_si128((__m128i *)&t[i+3][j]); 

     __t0 = _mm_unpacklo_epi32(row0, row1); 
     __t1 = _mm_unpacklo_epi32(row2, row3); 
     __t2 = _mm_unpackhi_epi32(row0, row1); 
     __t3 = _mm_unpackhi_epi32(row2, row3); 

     /* values back into I[0-3] */ 
     row0 = _mm_unpacklo_epi64(__t0, __t1); 
     row1 = _mm_unpackhi_epi64(__t0, __t1); 
     row2 = _mm_unpacklo_epi64(__t2, __t3); 
     row3 = _mm_unpackhi_epi64(__t2, __t3); 

     _mm_store_si128((__m128i *)&c_tra[j][i], row0); 
     _mm_store_si128((__m128i *)&c_tra[j+1][i], row1); 
     _mm_store_si128((__m128i *)&c_tra[j+2][i], row2); 
     _mm_store_si128((__m128i *)&c_tra[j+3][i], row3); 



    } 
} 

大會生成的代碼:

.L39: 
    lea rcx, [rsi+rdx] 
    movdqa xmm1, XMMWORD PTR [rdx] 
    add rdx, 16 
    add rax, 2048 
    movdqa xmm6, XMMWORD PTR [rcx+rdi] 
    movdqa xmm3, xmm1 
    movdqa xmm2, XMMWORD PTR [rcx+r9] 
    punpckldq xmm3, xmm6 
    movdqa xmm5, XMMWORD PTR [rcx+r10] 
    movdqa xmm4, xmm2 
    punpckhdq xmm1, xmm6 
    punpckldq xmm4, xmm5 
    punpckhdq xmm2, xmm5 
    movdqa xmm5, xmm3 
    punpckhqdq xmm3, xmm4 
    punpcklqdq xmm5, xmm4 
    movdqa xmm4, xmm1 
    punpckhqdq xmm1, xmm2 
    punpcklqdq xmm4, xmm2 
    movaps XMMWORD PTR [rax-2048], xmm5 
    movaps XMMWORD PTR [rax-1536], xmm3 
    movaps XMMWORD PTR [rax-1024], xmm4 
    movaps XMMWORD PTR [rax-512], xmm1 
    cmp r11, rdx 
    jne .L39 

gcc -Wall -msse4.2 -masm="intel" -O2 -c -S skylake linuxmint

-mavx2-march=naticve生成VEX編碼:vmovaps

回答

6

功能上這些指令是相同的。 我不喜歡複製粘貼+其他人陳述礦山這麼幾個環節解釋它:

Difference between MOVDQA and MOVAPS x86 instructions?

https://software.intel.com/en-us/forums/intel-isa-extensions/topic/279587

http://masm32.com/board/index.php?topic=1138.0

https://www.gamedev.net/blog/615/entry-2250281-demystifying-sse-move-instructions/

短版:

因此,大多數情況下,您應該嘗試使用 寄存器中您要使用的操作對應的移動指令。但是,還有一個額外的複雜因素。負載和內存的存儲在整數 和浮點單元的單獨端口上執行;因此從存儲器加載到 寄存器或從寄存器存儲到存儲器的指令將經歷相同的延遲,無論您附加到移動的數據類型如何。 因此 在這種情況下,movaps,movapd和movdqa將具有相同的延遲,沒有 與您使用的數據有關。由於movaps(和movups)編碼爲 二進制形式,比其他兩個字節少一個字節,因此 對所有reg-mem移動都有用,無論數據類型如何。

所以這是GCC優化。

+1

這實際上是英特爾和AMD推薦的代碼生成實踐。事實上,對於現代CPU,英特爾建議您始終使用''movups'',因爲對齊和未對齊的加載具有相同的性能 - 對齊的寫入更重要。請參閱[Intel](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html)和[AMD](http: //developer.amd.com/resources/developer-guides-manuals/)軟件優化指南。 –

+0

@ChuckWalbourn自從Nehalem以來,'movups'和'movaps'只有相同的表現。但即使這是誤導性的,因爲'movups'不能摺疊操作,因此只有'vmovaps'已經過時。那麼你確定這是英特爾和AMD的建議嗎?如果你的硬件支持它,它們可能意味着總是使用'vmovups'。 –

+0

@ChuckWalbourn我搜索了您指向的英特爾手冊,但沒有找到您提到的建議。你指的是哪一部分。我還搜索了'vmovaps',並在代碼中顯示了幾次,所以即使英特爾仍在使用它。 –

相關問題