avx2, simd 쪼렙임.
gcc를 그냥 빌드 하면 simd로 빌드된다. 따라서 128비트짜리 xmm 레지스터를 사용한다. gcc에 -march=haswell을 넣고 빌드하면 avx2로 빌드된다. 256비트짜리 ymm 레지스터를 사용할 수 있다.
... 하 글쓰다 날렸다 ... 소스 생략하고 요약하면 이렇다.
.
    movq    $a, -40(%rbp)
    movq    $b, -32(%rbp)
    movq    $c, -24(%rbp)
    movq    -40(%rbp), %rax
    vmovdqa (%rax), %ymm1
    movq    -32(%rbp), %rax
    vmovdqa (%rax), %ymm0
    vpaddd  %ymm0, %ymm1, %ymm0
    movq    -24(%rbp), %rax
    vmovdqa %ymm0, (%rax)
    addq    $32, -40(%rbp)
    addq    $32, -32(%rbp)
    addq    $32, -24(%rbp)
    movq    -40(%rbp), %rax
    vmovdqa (%rax), %ymm1
    movq    -32(%rbp), %rax
    vmovdqa (%rax), %ymm0
    vpaddd  %ymm0, %ymm1, %ymm0
    movq    -24(%rbp), %rax
    vmovdqa %ymm0, (%rax). . .
원본 c는 이렇다.
#include <stdio.h>
typedef int v4si __attribute__ ((vector_size (32)));
int a[] = {1,2,3,4,5,6,7,8, 2,3,4,4,5,6,7,8};
int b[] = {2,3,4,4,5,6,7,8, 2,3,4,4,5,6,7,8};
int c[16];
int main(int argc, char *argv[])
{
     v4si *ap, *bp, *cp;
     ap = a;
     bp = b;
     cp = c;
     *cp = *ap + *bp;
     ap++, bp++, cp++;
     *cp = *ap + *bp;
     printf("%d\n", c[0]);
}...
얼라인을 맞추지 않고 그냥 날 계산을 때리면 메모리 복사를 한다고 낑낑댄다..
    movq    a(%rip), %rax
    movq    %rax, -304(%rbp)
    movq    a+8(%rip), %rax
    movq    %rax, -296(%rbp)
    movq    a+16(%rip), %rax
    movq    %rax, -288(%rbp)
    movq    a+24(%rip), %rax
    movq    %rax, -280(%rbp)
    movq    a+32(%rip), %rax
    movq    %rax, -272(%rbp)
    movq    a+40(%rip), %rax
    movq    %rax, -264(%rbp)
    movq    a+48(%rip), %rax
    movq    %rax, -256(%rbp)
    movq    a+56(%rip), %rax
    movq    %rax, -248(%rbp)
    movq    b(%rip), %rax
    movq    %rax, -240(%rbp)
    movq    b+8(%rip), %rax
    movq    %rax, -232(%rbp)
    movq    b+16(%rip), %rax
    movq    %rax, -224(%rbp)
    movq    b+24(%rip), %rax
    movq    %rax, -216(%rbp)
    movq    b+32(%rip), %rax
    movq    %rax, -208(%rbp)
    movq    b+40(%rip), %rax
    movq    %rax, -200(%rbp)
    movq    b+48(%rip), %rax
    movq    %rax, -192(%rbp)
    movq    b+56(%rip), %rax
    movq    %rax, -184(%rbp)
    vmovdqa -304(%rbp), %ymm1
    vmovdqa -240(%rbp), %ymm0
    vpaddd  %ymm0, %ymm1, %ymm1
    vmovdqa -272(%rbp), %ymm2
    vmovdqa -208(%rbp), %ymm0
    vpaddd  %ymm0, %ymm2, %ymm0
    vmovdqa %ymm1, -464(%rbp)
    vmovdqa %ymm0, -432(%rbp)
    movq    -464(%rbp), %rax
    movq    %rax, -176(%rbp)
    movq    -456(%rbp), %rax
    movq    %rax, -168(%rbp)
    movq    -448(%rbp), %rax
    movq    %rax, -160(%rbp)
    movq    -440(%rbp), %rax
    movq    %rax, -152(%rbp)
    movq    -432(%rbp), %rax
    movq    %rax, -144(%rbp)
    movq    -424(%rbp), %rax
    movq    %rax, -136(%rbp)
    movq    -416(%rbp), %rax
    movq    %rax, -128(%rbp)
    movq    -408(%rbp), %rax
    movq    %rax, -120(%rbp)
    movq    -176(%rbp), %rax
    movq    %rax, -368(%rbp)
    movq    -168(%rbp), %rax
    movq    %rax, -360(%rbp)
    movq    -160(%rbp), %rax
    movq    %rax, -352(%rbp)
    movq    -152(%rbp), %rax
    movq    %rax, -344(%rbp)
    movq    -144(%rbp), %rax
    movq    %rax, -336(%rbp)
    movq    -136(%rbp), %rax
    movq    %rax, -328(%rbp)
    movq    -128(%rbp), %rax
    movq    %rax, -320(%rbp)
    movq    -120(%rbp), %rax
    movq    %rax, -312(%rbp)...
#include <stdio.h>
typedef int v4si __attribute__ ((vector_size (64)));
v4si a = {1,2,3,4,5,6,7,8, 2,3,4,4,5,6,7,8};
v4si b = {2,3,4,4,5,6,7,8, 2,3,4,4,5,6,7,8};
int main(int argc, char *argv[])
{
     printf("hello world\n");
     v4si c = a + b;
     printf("%d\n", c[0]);
}O2를 붙여보니 내용은 좀 달라지지만 맥락은 같다. 결과적으로 보면, 그냥 C 프로그래밍 할 때와 마찬가지다. 포인터로 다루면 메모리 복사를 회피할 수 있다. 끗.
+추가 : 아이러니컬 하게도 캐시라인은 보통 64바이트다. 64바이트 이하의 전역 데이터를 다룰때 자칫하면 거짓공유에 빠질 수 있다. 전역 데이터의 멀티 코어 공유가 있다면 64바이트 패딩을 치고 avx2를 쓰는 쪽이 아무것도 하지 않은 것에 비해 수십 배는 빠를 것 같다.
+추가 : 마찬가지로 avx2를 이용하여 memcpy를 구현하면 64바이트 단위로 복사를 구현할 수 있다. 물론 현재의 memcpy는 최적화가 다방면으로 잘 되어 있다고 들었다. 일반적인 용도로 memcpy를 재구현할 필요는 없으나, 대규모 벡터 연산이 필요한 경우 avx2를 응용한 포인터 연산을 고려해 볼 수 있다.