2017-02-15 7 views
2

다음 루프는 정수 행렬을 다른 정수 행렬로 변환합니다. 흥미롭게 컴파일 할 때 출력 결과를 출력 행렬에 저장하는 명령어 인 movaps을 생성합니다. 왜 gcc이 이것을할까요?왜이 SSE2 프로그램 (정수)이 무브 랩 (float)을 생성합니까?

데이터 :

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

루프 :

.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

:

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); 



    } 
} 

어셈블리 코드를 생성

-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의 경우 Intel은 정렬 된 & 정렬되지 않은로드가 동일한 성능 정렬의 쓰기가 더 중요하기 때문에 항상 "''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'movups'와'movaps'는 Nehalem 이후로만 동일한 성능을 가지고 있습니다. 그러나 'movups'가 조작을 폴드 할 수 없기 때문에 오해의 소지가 있습니다. 실제로 'vmovaps'만이 쓸모 없습니다. 인텔과 AMD의 추천이 맞습니까? 하드웨어가 지원한다면'vmovups '를 항상 사용하는 것일 수도 있습니다. –

+0

@ChuckWalbourn 지적한 인텔 설명서를 검색했지만 언급 한 권장 사항을 찾지 못했습니다. 어떤 부분을 언급하고 있습니까? 나는 또한'vmovaps'를 검색했는데 코드에 여러 번 표시되어 인텔이 여전히 그것을 사용합니다. –