티스토리 뷰


 Volatile

1. 요약

Volatile은 ‘휘발성의’ 라는 뜻을 가지고 있습니다.

Volatile 키워드를 사용해서 정의한 변수는 그것을 사용하는 문장(statement)외에 다른 것에 의해서 변경될 수 있다는 의미를 갖습니다.

다른 것이란 운영체제, 하드웨어 혹은 다른 스레드가 될 수 있습니다. 그러므로 Volatile 키워드를 사용해서 정의한 변수는 눈에 보이는 문장만을 상대로 함부로 최적화 시키지 말아야 함을 의미합니다.

이 글에서는 간단한 사용법과 Volatile 키워드의 유무에 따라서 컴파일러가 생산해내는 코드가 어떻게 달라지는지 알아보겠습니다.


2. 본문

(1) 사용법

사용법은 간단합니다.


volatile int k; 이제 k는 컴파일러가 함부로 최적화 시키지 않습니다.

(2) Loop를 돌며 일하는 Worker Thread

프로세스가 생성되면서 함께 생성되는 메인 스레드가 아니라면, 대부분의 경우는 메인스레드가 신호를 보내줄 때까지 루프를 돌면서 반복작업을 합니다.

다음과 같은 스레드 입구(Entry)함수가 있다고 합시다.


void ThreadEntry(void* pParm)

{

    bool* pbExit = static_cast<bool*>(pParm);

    int i = 0;

   

    while( !*pbExit)

    {

        i++;

    }

   

    printf("i = %d\n" ,i);

}


메인 스레드가 넘겨준 bool 변수가 true값을 가질 때까지 계속해서 i의 값을 1씩 더하는 루틴입니다.
*pbExit가 true가 되는 순간에 while문을 빠져 나오고 이 워커 스레드( 이렇게 부르기로 합시다)는 종료하게 됩니다.

물론 Debug 모드로 컴파일 하신다면 위의 시나리오대로 잘 작동할 겁니다.
하지만 Release 모드로 컴파일 하신다면 아마도 워커 스레드는 정상으로 종료할 수 없을 겁니다.

그 이유는 Release 모드에서는 컴파일러가 최적화를 하기 때문입니다. 위의 코드를 다시 보신다면, 함수 내에서 *pbExit 의 값을 변경시키는 부분( 읽는 부분만 있죠)은 없다는 것을 알 수 있습니다.

그 결과 컴파일러는 굳이 *pbExit의 값을 매번 비교할 필요가 없다고 생각하고는 *pbExit가 true인지 비교하는 코드를 제거해버린 것입니다.

해결책은 다음과 같습니다.


volatile bool* pbExit = static_cast<volatile bool*>(pParm);
이제 컴파일러는 섣불리 최적화하지 않을 것이고, Release 모드에서 정상 종료하는 좋은 코드가 되었습니다.
volatile의 유무에 따라 컴파일러가 생산하는 코드가 3. 예제 코드에 있으니 참조하시기 바랍니다.

Release 모드에서 어셈블된 코드를 보시려면 컴파일러 옵션에서 /FAcs를 추가하시면 됩니다. 그러면 *.cod라는 이름의 파일이 생성됩니다.


3. 예제 코드

(1) Volatile없는 경우


; 12   : bool* pbExit = static_cast<bool*>(pParm);

; 13   : int i = 0;

; 14   :

; 15   : while( !*pbExit)


  00000 8b 4c 24 04 mov ecx, DWORD PTR _pParm$[esp-4]

  00004 33 c0 xor eax, eax

  00006 80 39 00 cmp BYTE PTR [ecx], 0

  00009 75 03 jne SHORT $L42424

$L42423:


; 16   : {

; 17   : i++;


  0000b 40 inc eax


; 12   : bool* pbExit = static_cast<bool*>(pParm);

; 13   : int i = 0;

; 14   :

; 15   : while( !*pbExit)

//////////////////////////////////////////////////////////

// 이 부분 보이시죠? 비교도 안하고 무조건 점프하는 모습!!

  0000c eb fd jmp SHORT $L42423 ->

$L42424:


; 18   : }


(2) Volatile있는 경우


; 12   : volatile bool* pbExit = static_cast<volatile bool*>(pParm);

; 13   : int i = 0;

; 14   :

; 15   : while( !*pbExit)


  00000 8b 4c 24 04 mov ecx, DWORD PTR _pParm$[esp-4]

  00004 33 c0 xor eax, eax

  00006 80 39 00 cmp BYTE PTR [ecx], 0

  00009 75 07 jne SHORT $L42424

$L42423:


; 16   : {

; 17   : i++;


0000b 40 inc eax

////////////////////////////////////////////////

// 이 부분 보이시죠?? 비교해서 분기하는 모습!!

  0000c 8a 11 mov dl, BYTE PTR [ecx]

  0000e 84 d2 test dl, dl

  00010 74 f9 je SHORT $L42423

$L42424:


; 18   : }


- 2001.08.06 Smile Seo - 



공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함