volatile과 restrict는 둘 다 최적화 주변에 있지만 해결하는 문제는 완전히 다릅니다.
숏컷 코드
volatile: 이 메모리 접근을 최적화로 함부로 제거하거나 합치지 말라restrict: 이 포인터들은 서로 겹치지 않는다고 가정해도 된다
volatile uint32_t *status = (volatile uint32_t *)0x40000000;
void copy_fast(int * restrict dst, const int * restrict src, int n);역할 구분
즉 하나는 "접근을 보존"하는 쪽이고, 다른 하나는 "더 공격적으로 최적화해도 된다"는 약속입니다.
문법
- 메모리 매핑 I/O
- 하드웨어 레지스터
- 시그널 핸들러와 공유하는 플래그
volatile uint32_t *status = (volatile uint32_t *)0x40000000;
while ((*status & 0x01u) == 0) {
}여기서 중요한 것은 값이 "코드 바깥"에서 바뀔 수 있다는 점입니다. volatile은 그래서 접근 자체를 보존하는 데 쓰이지만, 원자성이나 스레드 간 동기화를 대신해 주지는 않습니다.
시그널 핸들러와 공유하는 단순 플래그는 보통 volatile sig_atomic_t처럼 다루지만, 멀티스레드 동기화는 volatile로 해결되지 않습니다. 원자성이나 메모리 순서를 보장하지 않기 때문입니다.
즉 thread-safe counter 같은 문제는 _Atomic이나 락 영역입니다.
void copy_fast(int * restrict dst, const int * restrict src, int n) {
for (int i = 0; i < n; i++) {
dst[i] = src[i];
}
}이 선언은 컴파일러에게 dst와 src가 같은 메모리를 가리키지 않는다고 말하는 셈입니다. 그래서 더 공격적인 최적화를 시도할 수 있습니다.
선택 기준
- 하드웨어/시그널 쪽 접근 보존:
volatile - 포인터 비겹침 약속:
restrict - 멀티스레드 동기화는
volatile아님 - 겹치는 복사는
restrict보다memmove쪽 - 실제로 겹치는 포인터에
restrict를 붙이면 안 된다
주의할 점
volatile과 restrict는 둘 다 "성능/최적화" 주변 키워드라 같이 묶여 보이지만, 실제로는 반대 방향의 제약을 줍니다. 의미를 정확히 모른 채 붙이면 성능도, 정확성도 둘 다 잃기 쉽습니다.
참고 링크
1 sources