• volatile
volatile 키워드는 컴퍼일러의 최적화를 제한하는 역할을 한다. volatile(휘발성)이라는 뜻 그대로 언제든지 그 값이 변할 수 있음을 명시하는 키워드이다. volatile로 지정된 변수는 최적화를 수행하지 않게 된다. 주로 메모리 맵 입출력(MMIO)를 제어할 때, volatile을 사용하여 컴파일러 최적화를 제한한다.
최적화 이전
최적화 이후: 0 != 255는 블록 내에서 true이므로 최적화되어 무한 루프에 빠진다.
volatile 사용: 변수 foo에 volatile 키워드를 지정하여, 최적화를 막을 수 있다.
• restrict
restrict 포인터는 메모리 접근에 관련된 최적화 기능이다(C99 표준). restrict 포인터는 컴파일러에게 최적화를 하라고 알려준다.
void increase(int *a, int *b, int *x) {
*a += *x; // 0: 8b 02 mov (%rdx),%eax
// 2: 01 07 add %eax,(%rdi)
*b += *x; // 4: 8b 02 mov (%rdx),%eax
// 6: 01 06 add %eax,(%rsi)
// 8: c3 retq
}
이처럼 같은 메모리 공간을 가리키는 포인터를 에일리어스(alias)라고 부르는데, 메모리가 같은 공간에 접근하는지 확인하여 처리하고, 잘못 처리했을 경우 되돌리는 작업은 상당히 복잡하고 비용이 많이 든다(성능이 떨어짐).
void increase(int *restrict a, int *restrict b, int *restrict x) {
*a += *x; // 0: 8b 02 mov (%rdx),%eax
// 2: 01 07 add %eax,(%rdi)
*b += *x; // 4: 01 06 add %eax,(%rsi)
// 6: c3 retq
}
잘 보면 *b += *x;의 어셈블리 명령 1줄이 줄어든 것을 알 수 있다. restrict 키워드를 보고 컴파일러가 최적화를 한 것. restrict 포인터는 각 포인터가 서로 다른 메모리 공간을 가리키고 있고, 다른 곳에서 접근하지 않으니 컴파일러가 최적화를 하도록 알려준다. 그러나 메모리가 다른 공간을 가리킨다고 보장한다거나 메모리 공간을 검사하는 용도는 아니다.
• constexpr
constexpr은 변수, 함수, 클래스를 컴파일 타임에 정수로 사용할 수 있게 해준다. 즉, 상수로 취급할 수 있는 작업은 컴파일 타임에 계산(최적화)해버리는 것이다.
이렇게 함수 자체에 constexpr 키워드를 주어, 컴파일 시간에 주어진 expression들을 미리 계산해버릴 수 있다. 이는 물론 재귀함수에도 적용이 가능하다.
참조