volatile 이 최적화 장벽 역할을 하나요?

gurugio의 이미지

리눅스 커널의 이해 원서의 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://skcho.com/82

http://monac.egloos.com/2185756

제가 알기로는 volatile 변수가 처리되는 코드만 확실히 메모리에 쓰기때문에
그 변수에만 장벽 역할을 하고
전체적인 최적화에 대해서는 장벽 역할을 안하는 것으로 알고 있습니다만 확실한가요?
따라서 최적화 장벽은 volatile 역할을 하지만
volatile이 최적화 장벽은 아니다라고 알고 있습니다.

아니면 volatile 변수가 사용되는 코드는 무조건 최적화 장벽 역할을 하게 되는 건가요?

어떻게 하면 실험->증명을 해볼 수 있는지 고민하고 있습니다...

freestyle의 이미지

보편적으로 volatile은 캐쉬 등이 되지 않기에
(최적화 작업에 의해 영향을 받지 않고)프로그래머가 쓴 용도대로 작용하기에,

volatile로 선언된 변수 자체뿐만 아니라
그 변수와 연관된 좀더 큰 범위의 최적화도 막지 않나 추측해 봅니다.

어떤 동작을 하는 코드인가와 컴파일러 최적화 특성에 따라서 '항상'
최적화의 걸림돌이 되지는 않지만,
최적화 과정으로 생기는 예기치 못한 오작동을 막기 위해
일정부분 포기 한다고 알고 있습니다.
- - - - - - - - - - - - - - - - - - - - - - - - -
Go to the U-City

----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------

grassman의 이미지

C99 표준에서 volatile은 다음과 같이 언급되어 있습니다.

At sequence points, volatile objects are stable in the sense that previous accesses are
complete and subsequent accesses have not yet occurred.

즉, sequence point에 도달했을 때 volatile object에 대한 모든 동작을 완료해야 합니다.

또한, sequence point는 다음과 같이 정의됩니다.

— The call to a function, after the arguments have been evaluated (6.5.2.2).
— The end of the first operand of the following operators: logical AND && (6.5.13);
logical OR || (6.5.14); conditional ? (6.5.15); comma , (6.5.17).
— The end of a full declarator: declarators (6.7.5);
— The end of a full expression: an initializer (6.7.8); the expression in an expression
statement (6.8.3); the controlling expression of a selection statement (if or switch)
(6.8.4); the controlling expression of a while or do statement (6.8.5); each of the
expressions of a for statement (6.8.5.3); the expression in a return statement
(6.8.6.4).
— Immediately before a library function returns (7.1.4).
— After the actions associated with each formatted input/output function conversion
specifier (7.19.6, 7.24.2).
— Immediately before and immediately after each call to a comparison function, and
also between any call to a comparison function and any movement of the objects
passed as arguments to that call (7.20.5).

따라서 예를들어 다음과 같은 코드가 있다고 하면

int main(void)
{
    volatile int a;
 
    a = 1;
 
    return 0;
}

컴파일러는 '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에 의해 바뀌는 것은 가정하고 있어야 하는 것으로 보입니다.

prio의 이미지

전에도 비슷한 내용의 스레드가 있었던 것 같은데..

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에는 치명적인 해를 끼치지만. -_-;;)

shkim의 이미지

멀티코어 멀티쓰레드 프로그램에서 volatile 은 믿을게 못됩니다.
컴파일러가 잘 만들어놔도 CPU 가 리오더링을 한다고 하는군요 -_-
메모리배리어 효과에 대해 확실히 이해하고 그것을 잘 써야겠습니다.

cinsk의 이미지

앞뒤 context를 몰라서 대충 추측해보면 asm volatile ...의 volatile을 의미하는 것인가 봅니다. 이 경우에 관한것은 GCC info document에 잘 나와 있습니다.

$ info gcc "c ext" ext

일반 응용 프로그램인 경우, 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/

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.