2017-02-15 30 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',并在代码中显示了几次,所以即使英特尔仍在使用它。 –

相关问题