1. volatile
volatile 은 최적화를 수행하지 말라는 뜻이다.
1) 변수에 volatile 선언
volatile int num;
이렇게 변수 num을 volatile로 선언하면, 변수 num에 저장된 값은 순간적으로 다른 영역
으로부터 참조될 수 있으니 코드 최적학를 수행하면 안된다는 의미를 준다.
이와 같은 코드가 있다고 가정할 때 컴파일러는 변수 num에 저장된 값을 성능의 향상을 위해서 한
번에 60 증가시켜도 되겠다라고 생각할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 | int num=20; // 전역변수 num int function(void) { AAA(10); // AAA 함수는 변수 num의 값을 변경 및 참조하지 않음 num+=10; BBB(20); // BBB 함수는 변수 num의 값을 변경 및 참조하지 않음 num+=20; CCC(30); // CCC 함수는 변수 num의 값을 변경 및 참조하지 않음 num+=30; ..... } | cs |
왜냐하면 num의 값이 10 증가 된 이후 20이 증가될 때까지, 그리고 다시 30이 증가될 때까지 값
의 참조가 어디에서도 이뤄지지 않고 있기 때문이다. 따라서 컴파일러는 위의 코드를 다음과 같은
형태로 바꿔서 컴파일을 하기도 하는데, 이는 컴파일러의 코드 최적화 기능에 따른 것이다.
1 2 3 4 5 6 7 8 9 10 | int num=20; // 전역변수 num int function(void) { AAA(10); // AAA 함수는 변수 num의 값을 변경 및 참조하지 않음 BBB(20); // BBB 함수는 변수 num의 값을 변경 및 참조하지 않음 CCC(30); // CCC 함수는 변수 num의 값을 변경 및 참조하지 않음 num+=60; ..... } | cs |
코드 최적화가 아주 잘 진행되었다. 그런데 이러한 코드 최적화가 문제가 되는 상황이 존재한다.
다른 영역의 프로그램 또는 하드웨어를 통해서 변수 num에 저장된 값이 참조되는 경우이다.
즉 변수 num에 저장된 값이 10 증가되고 나서 20이 증가되기 이전에 순간적으로 다른 영역으로부
터 값이 참조될 수 있다. 이러한 경우에는 위의 코드 최적화가 치명적인 문제가 된다.
따라서 volatile 선언이 필요하다. 참고로 이렇게 volatile로 선언된 변수 값의 변경은 항상 메인 메
모리에 반영이 된다. volatile 선언이 없는 변수는 성능의 향상을 위해서 값의 변경이 캐쉬 메모리
또는 레지스터에만 반영되었다가, 뒤늦게 메인 메모리에 반영되기도 한다. 그리고 volatile 로 선언
된 변수의 값은 참조될 때에도 캐쉬가 아닌 메인 메모리로부터 참조가 이뤄진다. 정리하면 volatile
로 선언된 변수는 캐쉬 메모리를 거치지 않는다. 다른 영역으로부터 언제 어떠한 형태로 값의 참조
가 이뤄질지 모르기 때문에 값의 변경이 실시간으로 메인 메모리에 반영이 된다.
2) 포인터에 volatile 선언
포인터 변수가 다른 영역에서(다른 영역의 프로그램 또는 하드웨어로부터)참조될 수 있음을 의미
하는 것이 아니라, 포인터가 가리 키는 메모리 공간이 다른 영역으로부터 참조될 수 있음을 의미하
는 것이다. 따라서 volatile로 선언된 포인터 변수의 * 연산은 코드 최적화의 대상에서 제외가 된다.
예제를 보자
1 2 3 4 5 6 7 8 | int function(void) { int * ptr; ptr=&numl; ptr=&num2; *ptr=10; *ptr=20; return *ptr; } | cs |
여기서 포인터 ptr이 volatile로 선언되면,다음과 같은 형태로 최적화가 진행된다.
1 2 3 4 5 6 7 8 | int function(void) { volatile int * ptr; ptr=&num2; *ptr=10; *ptr=20; return *ptr; } | cs |
포인터 ptr이 가리키는 메모리 공간의 접근에 대한 최적화가 수행되면 안되기 때문에 * 연산에 대
해서는 최적화가 수행되지 않는다. 그리고 포인터 변수가 다른 영역에서 참조될 수 있음을 의미하
는 것이 아니기 때문에 포인터 변수 ptr은 4행과 같이 최적화 되었다.
2. restrict
restrict는 volatile과 반대의 성격을 지닌다. restrict는 오히려 코드 최적화를 유도하는 선언이다.
그리고 restrict 선언은 다음과 같은 형태로 포인터에만 선언할 수 있다.
int * restrict ptr;
포인터의 특성을 설명하는 선언이기 때문이다.
restrict 선언은 컴파일러에게 이 포인터가 가리키는 메모리 공간은 이 포인터만으로 접근이 가능
한 영역이다라는 힌트를 제공해서 코드 최적화에 활용할 수 있도록 돕는다.
- 부적절한 예제를 봐보자
1 2 3 4 5 | int main(void) { int num=10; int * restrict ptr=# } | cs |
이 때 포인터 ptr을 restrict로 선언하는 것은 적절치 않다. 왜냐하면 포인터 ptr을 이용해서 접근 가능
한 메모리 공간은 num이라는 변수 이름으로도 접근이 가능하기 때문이다.
- 적절한 예제를 봐보자
1 2 3 4 5 6 | int main(void) { int * restrict ptr = (int * )malloc(sizeof(int)); .... } | cs |
다음과 같이 선언된 포인터 ptr은 restrict로 선언하기에 적절하다. 왜냐하면 malloc 함수를 통해서 할당
된 메모리 공간은 포인터 ptr을 통해서만 접근이 가능하기 때문이다. 이렇게 포인터가 restrict로 선언되
면, 해당 포인터가 가리키는 메모리 영역의 접근 연산은 캐쉬 메모리를 기반으로 최고의 성능을 낼 수
있도록 코드가 최적화된다.
'BackEnd > C' 카테고리의 다른 글
main 함수로의 문자열 전달 (0) | 2019.03.16 |
---|---|
메모리 컨트롤 함수 (0) | 2019.03.16 |
포인터의 const 선언 (0) | 2019.03.14 |
메모리 공간의 동적 할당 (1) | 2019.03.14 |
자료형에 이름을 부여하는 typedef 키워드 (0) | 2019.03.14 |