본문 바로가기

BackEnd/C

volatile 과 restict

반응형

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