volatile 이 최적화 장벽 역할을 하나요?
글쓴이: gurugio / 작성시간: 금, 2009/04/24 - 8:24오후
리눅스 커널의 이해 원서의 5.2.3 챕터에서 다음과 같은 말이 나옵니다.
The volatile keyword forbids the compiler to reshuffle the asm instruction with the other
instructions of the program.
최적화 장벽을 이야기하다가 뜬금없이 volatile 이야기를 왜하나..
그것도 왜 달랑 애매한 말로 한줄 써놓는건 뭥미??
조사를 해보니 더 모르겠습니다.
http://minjang.egloos.com/2274079
http://monac.egloos.com/2185756
제가 알기로는 volatile 변수가 처리되는 코드만 확실히 메모리에 쓰기때문에
그 변수에만 장벽 역할을 하고
전체적인 최적화에 대해서는 장벽 역할을 안하는 것으로 알고 있습니다만 확실한가요?
따라서 최적화 장벽은 volatile 역할을 하지만
volatile이 최적화 장벽은 아니다라고 알고 있습니다.
아니면 volatile 변수가 사용되는 코드는 무조건 최적화 장벽 역할을 하게 되는 건가요?
어떻게 하면 실험->증명을 해볼 수 있는지 고민하고 있습니다...
Forums:
보편적으로 volatile은
보편적으로 volatile은 캐쉬 등이 되지 않기에
(최적화 작업에 의해 영향을 받지 않고)프로그래머가 쓴 용도대로 작용하기에,
volatile로 선언된 변수 자체뿐만 아니라
그 변수와 연관된 좀더 큰 범위의 최적화도 막지 않나 추측해 봅니다.
어떤 동작을 하는 코드인가와 컴파일러 최적화 특성에 따라서 '항상'
최적화의 걸림돌이 되지는 않지만,
최적화 과정으로 생기는 예기치 못한 오작동을 막기 위해
일정부분 포기 한다고 알고 있습니다.
- - - - - - - - - - - - - - - - - - - - - - - - -
Go to the U-City
----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------
잘 알지는 못하지만 몇 자 적어봅니다.
C99 표준에서 volatile은 다음과 같이 언급되어 있습니다.
즉, sequence point에 도달했을 때 volatile object에 대한 모든 동작을 완료해야 합니다.
또한, sequence point는 다음과 같이 정의됩니다.
따라서 예를들어 다음과 같은 코드가 있다고 하면
컴파일러는 'The end of a full expression'이 sequence point이므로 a = 1;이 끝나는 지점에서 a라는 변수에 1을 대입한 상태여야 됩니다. 따라서 컴파일러는 a라는 변수가 사용되지 않음에도 삭제하지 않고 저 위치에 그대로 두게 됩니다. 만약 a = 1;의 앞 뒤로 volatile이 아닌 다른 변수의 대입문이 있었다면 컴파일러는 그 변수들의 대입순서는 최적화 할 것입니다.
중요한 것은 volatile의 구현은 implementation-specific으로 나타나 있다는 점입니다. 즉, 저러한 결과를 내기는 하는데 실제로 어떻게 행동해서 저런 결과가 나올지는 컴파일러 마음대로라는 것입니다.
리눅스 커널의 이해이므로 gcc 컴파일러의 관련 문서를 찾으면 다음과 같습니다.
http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
그러나 위에 언급된 것과 같은 내용은 발견할 수 없었습니다. asm() 내부의 instruction 순서가 바뀌지는 않겠지만 주변 명령의 순서가 dependency에 의해 바뀌는 것은 가정하고 있어야 하는 것으로 보입니다.
전에도 비슷한
전에도 비슷한 내용의 스레드가 있었던 것 같은데..
volatile 키워드는 gnu inline assembler 에서 쓰는 용도와, 일반적인 C/C++에서 쓰는 용도가 다릅니다.
gnu inline assembler에서는 위에 인용하신 문장 그대로,
volatile로 선언된 부분에 대해 컴파일러가 최적화를 하지 않습니다.
순서를 바꾸지 않는 것은 물론, nop instruction도 그대로 남겨 둡니다.
반면, C/C++에서는 메모리 load/store가 반드시 일어나도록 코드를 생성하라는 뜻입니다.
register 할당을 하지 않기 때문에 '최적화'에 연결시켜 생각하는 경우가 많으나,
엄밀하게는 최적화와는 별개라고 봐야 할 것입니다.
C/C++에서 volatile 변수의 memory barrier 역할과 관련해서는,
대강 다음처럼 설명할 수 있겠네요.
일반적으로 프로그램에서 volatile 키워드를 사용하는 빈번한 이유 중 하나가 스레드 간의 통신입니다.
스레드 간에 공유 변수를 두고 그 변수에 값을 읽고 쓰는 것으로 메시지를 전달하려고 하는데,
만약 그 변수가 레지스터에 할당이 되어버리면 스레드 간 공유되지 않기 때문에 메시지 전달이 되지 않겠지요.
그래서 volatile 키워드를 이용해 반드시 메모리에 값을 읽고/쓰게 만들어 줘야 합니다.
몇몇 컴파일러에서 volatile 키워드에 (표준에는 없는) memory barrier 기능을 부여하는 것은,
바로 위의 이유로 사용되는 경우를 돕기 위해서 입니다. (물론 다른 이유도 있겠습니다만.. ㅎㅎ)
예를 들어 producer/consumer 모델에서, producer가 버퍼에 값을 기록한 후에,
volatile variable로 consumer에게 값이 기록되었음을 알려준다고 하면,
volatile variable 에 쓰기 전에 버퍼 메모리에 값이 반드시 기록되어야 합니다.
사실 위 경우 버퍼에 write 하는 것과
volatile variable에 write 하는 것은 서로 독립적이기 때문에,
순서가 바뀌어도 상관이 없는 경우가 많습니다.
그런데 만약 이 순서를 컴파일러가 알아서 지켜준다고 하면,
앞의 예제의 경우 프로그래밍이 쉬워지는 경우가 생기게 되지요.
(portability에는 치명적인 해를 끼치지만. -_-;;)
대세는 멀티코어
멀티코어 멀티쓰레드 프로그램에서 volatile 은 믿을게 못됩니다.
컴파일러가 잘 만들어놔도 CPU 가 리오더링을 한다고 하는군요 -_-
메모리배리어 효과에 대해 확실히 이해하고 그것을 잘 써야겠습니다.
POSIX 호환 시스템인
앞뒤 context를 몰라서 대충 추측해보면 asm volatile ...의 volatile을 의미하는 것인가 봅니다. 이 경우에 관한것은 GCC info document에 잘 나와 있습니다.
일반 응용 프로그램인 경우, POSIX 호환 시스템인 경우, mutex를 쓰면 해결됩니다. signal handler, memory mapped I/O, setjmp/longjmp를 쓰지 않는다면, (asm volatile 제외) volatile keyword는 필요없습니다.
--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
댓글 달기