C언어 restrict 포인터
안녕하세요! 저는 C언에서 restrict 포인터에 대해서 글을 작성하고자 합니다.
Libft 과제중 memcpy 함수에서 restrict 포인터가 나오는데요. 구체적으로 restrict 포인터가 무슨 역활을 하는지 알고 싶었습니다.
이 글에서는 restrict 포인터가 어떤 의도를 가지고 만들어졌고, 올바르게 사용하는 방법에 대해 알아보고자 합니다.
1. restrict 키워드 에 대해서
먼저, restrict는 포인터 선언에 사용하는 키워드입니다. 포인터 변수명 앞에 restrict 키워드를 붙여서 사용하면 됩니다.
// 매개변수로 restrict 포인터 설정
void *memcpy(void *restrict dst, const void *restrict src, size_t n);
C
복사
2. restrict 포인터가 의미하는 것
restrict 포인터가 의미하는 것은 특정 메모리 영역에 접근 할 수 있는 포인터가 단 하나임을 보장하는 것으로, 프로그래머가 컴파일러에게 이 포인터 외엔 그 메모리 영역에 접근하는 수단이 없다고 미리 알려 컴파일러가 더 나은 최적화를 하도록 도와주는 것입니다.
3. restrict 포인터의 최적화 과정
restrict 포인터는 컴파일러가 어셈블리 코드를 만들때 메모리 접근 관련하여 최적화를 수행하게 해줍니다.
구체적으로 어떤 차이점이 있는지 알아보겠습니다.
•
restrict 포인터를 이용하지 않은 코드
/*
어셈블리 코드를 다음과 같이 이해하시면 좋을거 같습니다.
rdi = a
rsi = b
rdx = x
eax = temp
*/
void increase(int *a, int *b, int *x)
{
*a += *x;
mov (%rdx),%eax // x를 역참조하여 가져온 값을 eax에 저장
add %eax,(%rdi) // eax의 값만큼 a를 역참조하여 값을 증가시킴
*b += *x;
mov (%rdx),%eax // x를 역참조하여 가져온 값을 eax에 저장
add %eax,(%rsi) // eax의 값만큼 b를 역참조하여 값을 증가시킴
}
//출처 : C 언어 코딩 도장
C
복사
먼저, 어셈블리에 대해서 어느 정도 지식이 필요한데요. 어셈블리는 여러 레지스터(변수라 생각)를 이용해서 연산을 실시합니다.
위 코드에 보이는 eax는 우리가 코딩에서 자주 사용하는 temp 변수랑 비슷하다고 생각하시면 됩니다. eax 레지스터에 연산할 임의의 값을 저장하고, 그 값을 연산하는데 사용합니다.
restrict 포인터를 사용하지 않은 위 코드에서는 4줄의 어셈블리 코드가 생성되었습니다.
연산을 할때마다 x가 참조하는 값을 eax에 저장하고, 더하기 연산을 실행합니다.
그 이유는 연산 이후에 x의 값이 달라졌을 수 있기 때문에(a와 x가 같은 주소 값이면) eax에 다시 x의 값을 저장하는 것입니다.
하지만, a, b, x가 전부 다른 메모리 주소에 위치하는 포인터라면, x의 값이 달라졌을리가 없습니다.
이처럼 불필요하게 다시 메모리에 접근하는 어셈블리어를 없애주는것이 restrict 포인터 입니다.
•
restrict 포인터를 이용한 코드
void increase(int *restrict a, int *restrict b, int *restrict x)
{
*a += *x;
mov (%rdx),%eax // x를 역참조하여 가져온 값을 eax에 저장
add %eax,(%rdi) // eax의 값만큼 a를 역참조하여 값을 증가시킴
*b += *x;
add %eax,(%rsi) // eax의 값만큼 b를 역참조하여 값을 증가시킴
//출처 : C 언어 코딩 도장
C
복사
매개 변수를 모두 restrict 포인터로 설정하니 eax에 x값을 다시 저장하는 어셈블리 코드가 한줄 줄었습니다.
restrict 포인터로 설정하면, a와 b와 x가 모두 다른 메모리 공간을 참조하고 있다는 뜻입니다.
즉, 연산 이후에 x의 값이 변경되지 않기에, 다시 eax에 x값을 저장하지 않고 그대로 연산을 수행합니다.
4. restrict 포인터를 올바르게 사용하는 방법
restrict 포인터는 전적으로 프로그래머가 다른 주소값을 가지고 있는 포인터임을 확인하고 보장해야합니다. 컴파일러가 따로 확인 작업을 거치지 않기 때문입니다.
restrict 포인터를 이해하고 있는 프로그래머들은 함수의 원형만 보아도 어떤 동작을 수행하는지 가늠할 수 있기에 가독성 측면에서는 좋으나, 잘못 사용하면 에러 찾기도 어렵기에, 신중히 사용해야 할 것입니다.
참고 문헌