restrict 키워드는 C99 표준에서 도입된 포인터를 위한 한정자입니다. 이것은 컴파일러에게 포인터가 포인터의 유효 범위 내에서 메모리 객체에 접근하는 유일한 방법임을 알립니다. 이는 특히 큰 버퍼를 다룰 때 최적 컴파일러가 더 효율적인 머신 코드를 생성하는 데 큰 도움을 줍니다.
예를 들어:
void vector_add(int * restrict a, int * restrict b, int * restrict c, size_t n) { for (size_t i = 0; i < n; ++i) c[i] = a[i] + b[i]; }
여기서 가정하는 것은 배열 a, b, c가 겹치지 않는다는 것입니다. 이 요구사항을 위반하면 정의되지 않은 동작과 추적하기 어려운 오류가 발생할 수 있습니다.
restrict를 사용하는 것은 다른 포인터나 부수 경로가 같은 메모리를 가리키지 않는 것을 확신할 수 있을 때만 권장됩니다.
같은 메모리 값이 두 개의 restrict 포인터를 통해 동시에 가시적일 수 있습니까?
답변:
아니요, 이는 정의되지 않은 동작을 초래합니다. 컴파일러가 두 번째 포인터를 통해 수행된 변경 사항을 반영할 것이라는 보장은 없습니다. 예—비극적으로 잘못된 코드:
void f(int * restrict x, int * restrict y) { x[0] = 1; y[0] = 2; } int main() { int v; f(&v, &v); // restrict 조건 위반 }
이야기
금융 계산 코어에서 배열을 최적화하면서 restrict를 추가했지만, 배열들이 비즈니스 로직의 일부 요구에 따라 겹칠 수 있다는 것을 간과했습니다. 이로 인해 잘못된 균형 계산이 발생했습니다.
이야기
배치 행렬 곱셈 메서드가 restrict 적용 후 속도가 빨라졌지만, 한 반복에서 결과 배열이 입력 배열 중 하나와 교차하여 응답이 예측할 수 없게 되었습니다. 오류는 부하 테스트 중에만 포착되었습니다.
이야기
이미지 처리 함수 중 하나에서 같은 버퍼의 두 개의 조각에 대한 포인터가 우연히 restrict로 선언되었습니다. 컴파일러와 최적화기를 업데이트하자 이미지 처리 결과가 급격히 왜곡되었습니다 — 원인: 컴파일러가 캐시를 적극적으로 재사용하고 수정 사항을 무시하게 되었습니다.