_man(C99) restrict 포인터

2022. 3. 13. 15:30카테고리 없음

restrict 키워드

컴파일을 할 때 최적화를 위해서 사용하는 키워드이다.
어떤 방식으로 최적화 되는지 알아보기 위해 간단한 함수 두개를 비교해보자.

간단한 함수 작성

void increase(int *a, int *b, int *x) 
{
    *a += *x;
    *b += *x;
}

void restrict_increase(int *restrict a, int *restrict b, int *restrict x) 
{
    *a += *x;
    *b += *x;
}

포인터를 받아서 x만큼 더하는 간단한 함수이다. restrict를 사용했을 때 어셈블리어에 어떤 변화가 생겼을까?

obj 파일 분석

void increase(int *a, int *b, int *x) 
{
    *a += *x;
restrict.o[0x4] <+4>:  mov    eax, dword ptr [rdx]
restrict.o[0x6] <+6>:  add    dword ptr [rdi], eax
    *b += *x;
restrict.o[0x8] <+8>:  mov    eax, dword ptr [rdx]
restrict.o[0xa] <+10>: add    dword ptr [rsi], eax
}

void restrict_increase(int *restrict a, int *restrict b, int *restrict x) 
{
    *a += *x;
restrict.o[0x14] <+4>:  mov    eax, dword ptr [rdx]
restrict.o[0x16] <+6>:  add    dword ptr [rdi], eax
    *b += *x;
restrict.o[0x18] <+8>:  add    dword ptr [rsi], eax
}

increase 함수는 a에 x를 더하기 위해서 x가 가리키는 주소를 레지스터에 복사하고, a에 더하고 있다. b도 마찬가지이다.

restrict_increase 함수는 a에서는 위와 같은 방식으로 작동하나, b에서는 레지스터에 다시 복사하지 않고 값을 재사용하여 b에 더하는 것을 볼 수 있다.

x의 주소값에 참조할 수 있는 방법이 x를 이용하는 방법밖에 없다고 알려주었으므로 중간에 메모리에 변화를 주는 연산을 해도 x에서 다시 값을 읽어올 필요가 없어진다. 따라서 restrict_increase 함수에서는 x주소에서 값을 받아오는 mov 연산을 빼고 기존의 값을 재사용 하는 것을 볼 수 있다.

excutable 파일 분석

void increase(int *a, int *b, int *x) 
{
restrict[0x100003f44] <+4>:  mov    qword ptr [rbp - 0x8], rdi
restrict[0x100003f48] <+8>:  mov    qword ptr [rbp - 0x10], rsi
restrict[0x100003f4c] <+12>: mov    qword ptr [rbp - 0x18], rdx
    *a += *x;
restrict[0x100003f50] <+16>: mov    rax, qword ptr [rbp - 0x18]
restrict[0x100003f54] <+20>: mov    ecx, dword ptr [rax]
restrict[0x100003f56] <+22>: mov    rax, qword ptr [rbp - 0x8]
restrict[0x100003f5a] <+26>: add    ecx, dword ptr [rax]
restrict[0x100003f5c] <+28>: mov    dword ptr [rax], ecx
    *b += *x;
restrict[0x100003f5e] <+30>: mov    rax, qword ptr [rbp - 0x18]
restrict[0x100003f62] <+34>: mov    ecx, dword ptr [rax]
restrict[0x100003f64] <+36>: mov    rax, qword ptr [rbp - 0x10]
restrict[0x100003f68] <+40>: add    ecx, dword ptr [rax]
restrict[0x100003f6a] <+42>: mov    dword ptr [rax], ecx
}

void restrict_increase(int *restrict a, int *restrict b, int *restrict x) 
{
restrict[0x100003f74] <+4>:  mov    qword ptr [rbp - 0x8], rdi
restrict[0x100003f78] <+8>:  mov    qword ptr [rbp - 0x10], rsi
restrict[0x100003f7c] <+12>: mov    qword ptr [rbp - 0x18], rdx
    *a += *x;
restrict[0x100003f80] <+16>: mov    rax, qword ptr [rbp - 0x18]
restrict[0x100003f84] <+20>: mov    ecx, dword ptr [rax]
restrict[0x100003f86] <+22>: mov    rax, qword ptr [rbp - 0x8]
restrict[0x100003f8a] <+26>: add    ecx, dword ptr [rax]
restrict[0x100003f8c] <+28>: mov    dword ptr [rax], ecx
    *b += *x;
restrict[0x100003f8e] <+30>: mov    rax, qword ptr [rbp - 0x18]
restrict[0x100003f92] <+34>: mov    ecx, dword ptr [rax]
restrict[0x100003f94] <+36>: mov    rax, qword ptr [rbp - 0x10]
restrict[0x100003f98] <+40>: add    ecx, dword ptr [rax]
restrict[0x100003f9a] <+42>: mov    dword ptr [rax], ecx
}

obj 파일에서는 최적화 되어 operation이 준 것을 볼 수 있었는데, 막상 executable 파일을 뜯어보니 x가 가리키는 주소를 b가 더해질 때도 복사를 한번 더 하고 있다.

왜 이런 차이가 있는지는 아직 모르겠다.