로컬 변수를 무조건 stack에 위치시키는 방법

ppappa의 이미지

int function()
{
    int x;

    ....
}

위와 같이 함수의 로컬 변수를 선언할 때
가능하면 register 변수로 자동 할당하는 것으로 알고 있습니다.

이 로컬 변수를 register 변수가 아닌 stack에 위치하게 하고 싶을 경우에 방법이 있을까요?

일단 int x 로 선언하고
그 다음에 어떤 함수인가를 부르면 된다는 말도 들은 것 같긴한데.. 모르겠네요.

고수님들 조언 한말씀 부탁합니다!

doldori의 이미지

x가 register로 할당되는지는 구현체에 따라 달라집니다만, & 연산자를 붙이면
register에 할당되지 않습니다. register 변수는 주소를 취할 수 없으니까요.

int x;
int* p = &x;
ixevexi의 이미지

volatile 키워드가 그런 역할을 하는게 아닌가요?

C++, 그리고 C++....
죽어도 C++

lovemyin의 이미지

volatile 키워드 맞아요.....

변수를 레지스터에 생성되지 못하게 막는게 volatile의 역할이죠...

/***************************************************
* 가장 심플한 것이 가장 아름다운 것이다.
***************************************************/

atie의 이미지

질문하신 것에 벗어나기는 하지만, volatile을 local 변수에 쓴다는 것이 자바를 쓰는 저에게는 이해가 안되는군요. volatile은 변수가 여러 thread에 의해 동시다발적으로 변경이 되는 경우에 레지스터에 캐싱을 하지말고 매번 새로운 값이 읽혀지도록 하기 위해 쓰는데 (자바에서는), local 변수를 다른 thread에서는 볼 수가 없는데 local 변수에 volatile을 지정할 수 있나요 (아니면 할 필요가 있나요)?

----
I paint objects as I think them, not as I see them.
atie's minipage

ixevexi의 이미지

atie wrote:
질문하신 것에 벗어나기는 하지만, volatile을 local 변수에 쓴다는 것이 자바를 쓰는 저에게는 이해가 안되는군요. volatile은 변수가 여러 thread에 의해 동시다발적으로 변경이 되는 경우에 레지스터에 캐싱을 하지말고 매번 새로운 값이 읽혀지도록 하기 위해 쓰는데 (자바에서는), local 변수를 다른 thread에서는 볼 수가 없는데 local 변수에 volatile을 지정할 수 있나요 (아니면 할 필요가 있나요)?

제가 알기론 volatile이 바로 이런경우에 쓰기 위해 만들어진걸로 알고 있습니다.

즉 어떤 변수가 메모리맵 장치를 다루는데 쓰이거나 다른 이유(다른 쓰레드)에 의해
변경될 상황이 있을때 필요하다고 들었습니다.

다만 volatile의 이런 특성상 컴파일러의 최적화를 막는데 이용이 된다고 알고 있습니다. 즉 이런 side effect를 이용하는 것이지요

전웅님의 명저 8) C언어 펀더멘탈 P263에서는 이렇게 말하고 있습니다.

volatile int *port;

volatile int *get_prot(void)
{
    /*특정 입출력 포트의 주소를 반환*/
}

int main(void)
{
    int i;

    port = get_port();
    for(i=0; i<20; i++){
        sum+=*port;
    }


    return 0;
}

똑똑한 컴파일러라면 sum+= *port 의 for문을
sum + (*port)*20으로 바꿀 것입니다.
다만 여기서는 port의 값이 계속 바뀌게 되므로
volatile을 써두어서 port의 값이 바뀔 수 있음을 컴파일러에게 알려주어
최적화를 막는 역할을 합니다....
위에서 port가 비록 전역이지만 main안에 들어가도(로컬 변수더라도) 상관은 없겠쬬?

역시 전웅님의 C언어 펜더멘탈은 바이블 -_-;;
어쨌건 바이블에서는 volatile한정어가 컴파일러의 임의의
최적화를 막아 사용자가 쓴 코드 그대로 추상적 머신이 동작하게
해준다 라고 결론을 내리고 있습니다.

C++, 그리고 C++....
죽어도 C++

atie의 이미지

ixevexi wrote:
atie wrote:
질문하신 것에 벗어나기는 하지만, volatile을 local 변수에 쓴다는 것이 자바를 쓰는 저에게는 이해가 안되는군요. volatile은 변수가 여러 thread에 의해 동시다발적으로 변경이 되는 경우에 레지스터에 캐싱을 하지말고 매번 새로운 값이 읽혀지도록 하기 위해 쓰는데 (자바에서는), local 변수를 다른 thread에서는 볼 수가 없는데 local 변수에 volatile을 지정할 수 있나요 (아니면 할 필요가 있나요)?

...

volatile int *port;

volatile int *get_prot(void)
{
    /*특정 입출력 포트의 주소를 반환*/
}

int main(void)
{
    int i;

    port = get_port();
    for(i=0; i<20; i++){
        sum+=*port;
    }


    return 0;
}

...
위에서 port가 비록 전역이지만 main안에 들어가도(로컬 변수더라도) 상관은 없겠ㅤㅉㅛㅤ?

...


...으로 대신한 부분은 저도 그렇게 알고 있습니다. 제가 궁금한 것은 로컬 변수더라도 상관은 없다고 하셨는데 volatile을 로컬 변수에 붙이는 것이 효과가 있냐는 질문입니다.

----
I paint objects as I think them, not as I see them.
atie's minipage

cinsk의 이미지

로컬이든 아니든 상관 없습니다.

Quote:
volatile은 변수가 여러 thread에 의해 동시다발적으로 변경이 되는 경우에 레지스터에 캐싱을 하지말고 매번 새로운 값이 읽혀지도록 하기 위해 쓰는데...

Thread를 여러 개 쓴 프로그램에서만 volatile이 의미가 있다..도 항상 맞는 말은 아닙니다. (여기서 thread를 여러 개 ㅤㅆㅓㅅ다는 것은 pthread와 같이 직접적으로 thread를 여러 개 쓰도록 작성한 코드로 가정합니다.)

volatile은 컴파일러가 알 수 없는 방법으로, 변경될 소지가 있는 것들에 volatile을 붙임으로써, 최적화?와 같은 작업이 일어나지 않고 코드 그대로 동작하게 강제하는 것입니다.

지역 변수가 하드웨어의 특정 번지를 나타내고 있다고 할 때, 여기에 volatile을 써서, 일어날 수 있는 문제를 막는 것이 보통입니다. volatile이 꼭 지역/전역에만 쓰일 수 있는 것인가? 그렇지 않습니다. volatile은 scope와 상관없이 쓸 수 있습니다.

atie의 이미지

역시 차이가 있다고 해야겠군요.

원래 질문과 상관없이 자바 이야기를 끌어들여 궁금한 것을 물어보았는데, 우선은 자바에서는 로컬 변수는 여러 쓰레드에서 접근이 불가능하기 때문에 로컬 변수에 volatile을 쓰지 않습니다.
그리고 최적화를 하지 않고 컴파일러에게 volatile을 써서 이 변수는 레지스터에다 캐싱을 하지말고 메모리 값을 복사해서 매번 값이 갱신하라고 까지 주의를 주는 경우는 그 변수가 프로그래머가 의도하지 않게 변경이 되는 것을 막고자 하는 경우일텐데 멀티 쓰레드가 대표적인 경우라 volatile는 concurrent 프로그래밍에 쓰는 것이 보통의 경우라 할 수 있습니다.

참고로, 이렇게 volatile을 자바에서 선언을 하고 사용을 의도하여도 제가 2년전 쯤에 테스트해본 1.4 JVM에서는 윈도우즈던 리눅스이던 실제로 동작은 되지 않았습니다. (1.5에서는 concurrent.automic 클래스가 volatile의 확장판이라고 할 수 있는데 테스트해 본적은 없습니다.)

실제 c나 c++에서 volatile을 써서 의도하는 결과를 얻을만큼 효과적인가요?

----
I paint objects as I think them, not as I see them.
atie's minipage

cinsk의 이미지

Quote:
실제 c나 c++에서 volatile을 써서 의도하는 결과를 얻을만큼 효과적인가요?

효과적이라기 보다는 volatile을 쓰지 않을 경우, 동작하지 않는 코드를 만들어내기는 쉽습니다. 특히 하드웨어를 직접 다룬다거나 multi-thread로 동작하거나 하는 app에서는..

왜냐하면,

{
  int *p = <hardware specific address>;
  *p = 3;
}

란 코드를 컴파일러가 볼 경우, *p에 저장된 값을 읽어가는 코드가 없기 때문에, 위의 코드는 최적화할 때, 무시될 확률이 큽니다. 이 경우, 원하는 곳에 3을 안 써 버리는 결과를 낳습니다.

purewell의 이미지

cinsk wrote:
Quote:
실제 c나 c++에서 volatile을 써서 의도하는 결과를 얻을만큼 효과적인가요?

효과적이라기 보다는 volatile을 쓰지 않을 경우, 동작하지 않는 코드를 만들어내기는 쉽습니다. 특히 하드웨어를 직접 다룬다거나 multi-thread로 동작하거나 하는 app에서는..

왜냐하면,

{
  int *p = <hardware specific address>;
  *p = 3;
}

란 코드를 컴파일러가 볼 경우, *p에 저장된 값을 읽어가는 코드가 없기 때문에, 위의 코드는 최적화할 때, 무시될 확률이 큽니다. 이 경우, 원하는 곳에 3을 안 써 버리는 결과를 낳습니다.

아, 맞아요. DOS에서 그래픽 메모리에 바로 쓰기를 하는데 저런 식으로 만든 함수가 작동하지 않아서 애를 먹인 경우가 있었습니다.
분명히 DEBUG모드에선 동작하는데 RELEASE모드에서는 동작하지 않는 멋지구리한 상황을 연출했죠.

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

pool007의 이미지

volatile 에 대해 오해하는 분들이 많은 듯 합니다... volatile은 컴파일러 수준에서 reordering을 막아주는 역할을 합니다. 하지만 '컴파일러' 수준만에서만 그렇고, 따라서 SMP를 사용하는 경우가 잦은 현대적인 의미에서의 멀티쓰레딩에서는 MMU 역시 reordering을 수행하므로 volatile이 별다른 의미가 없습니다. volatile은 C/C++의 일부이며, C/C++은 threading을 언어에 내장하고 있지 않습니다. 탄생부터 쓰레드를 고려했던 자바, C# 등의 언어와 다른 점이죠. C/C++ 에서 threading을 담당하는 것은 compiler가 아니라 POSIX 입니다.

이런 관점에서 앞의 분들의 글에 comment를 달아 보았습니다.

Quote:
즉 어떤 변수가 메모리맵 장치를 다루는데 쓰이거나 다른 이유(다른 쓰레드)에 의해 변경될 상황이 있을때 필요하다고 들었습니다.

'다른 쓰레드에 의해 변경될 상황이 있을때' 필요 없습니다. POSIX쓰레드를 쓰는 한이요. POSIX를 쓸 때 volatile은 동기화를 위한 필요충분 조건이 되지 못합니다. 문제는 SMP에서 발생합니다.

Quote:
어쨌건 바이블에서는 volatile한정어가 컴파일러의 임의의 최적화를 막아 사용자가 쓴 코드 그대로 추상적 머신이 동작하게 해준다 라고 결론을 내리고 있습니다.

그 C언어의 추상머신은 멀티 쓰레드를 인식하지 못합니다. 스펙에 없어요.

Quote:
Thread를 여러 개 쓴 프로그램에서만 volatile이 의미가 있다..도 항상 맞는 말은 아닙니다. (여기서 thread를 여러 개 ㅤㅆㅓㅅ다는 것은 pthread와 같이 직접적으로 thread를 여러 개 쓰도록 작성한 코드로 가정합니다.)

volatile은 컴파일러가 알 수 없는 방법으로, 변경될 소지가 있는 것들에 volatile을 붙임으로써, 최적화?와 같은 작업이 일어나지 않고 코드 그대로 동작하게 강제하는 것입니다.

멀티 쓰레드에서 volatile은 의미 없습니다.

Quote:
특히 하드웨어를 직접 다룬다거나 multi-thread로 동작하거나 하는 app에서는..

멀티 쓰레드에서 volatile은 의미 없습니다.

Quote:
참고로, 이렇게 volatile을 자바에서 선언을 하고 사용을 의도하여도 제가 2년전 쯤에 테스트해본 1.4 JVM에서는 윈도우즈던 리눅스이던 실제로 동작은 되지 않았습니다.

말씀하신대로 1.4까지 SUN의 JVM마저도 volatile을 제대로 구현하고 있지 않았습니다. 가장 큰 문제는 메모리 모델때문이었구요.

Quote:

1.5에서는 concurrent.automic 클래스가 volatile의 확장판이라고 할 수 있는데 테스트해 본적은 없습니다.

말씀하신대로 JDK 1.5에서의 AtomicInteger가 volatile의 확장입니다. AtomicInteger 의 경우에는 읽기/값변경/쓰기가 한번에 수행 될 수 있습니다. 반면, 자바의 volatile은 읽기 또는 쓰기만 한번에 수행될 수 있습니다. 이 말은,

volatile int a = 3;

a = a + 1; // this is not atomic. (read a, compute a+1, and store the result into a)
a++; // this isn't atomic, either. (ditto)

int b = a; // read operation is atomic.
a = 4; // also, atomic.

을 의미합니다.

Quote:
실제 c나 c++에서 volatile을 써서 의도하는 결과를 얻을만큼 효과적인가요?

반복해서 말하지만, 효과적이지 않습니다. volatile을 여러분이 생각하시는 의미대로 멀티 쓰레딩 환경에서의 atomic operation의 단위로 제대로 취급해주는 언어는 JAVA와 C#입니다.

다음은 이전에 제가 KLDP에 포스팅 했던 아래 글입니다.

Quote:

멀티 프로세서의 멀티 쓰레드 환경에서는 portable한 코드를 쓰고 싶다면 volatile을 사용해서는 안됩니다. 그리고 특히, C/C++에서 object에 대한 volatile 변수는 "undefined"되어 있어 동작을 보장할 수 없구요. (위의 예에서 객체에 대한 변수를 선언한건지 아닌지 잘 모르겠지만요.)

작년에 이쪽 이슈에 대해서 자바 커뮤니티내에서 논쟁이 있었습니다. 그때는 double checked locking 이 왜 불가능한가에대한 문제였는데, 설명하기도 일일히 대처하기도 너무 힘들었습니다. 그래서 여기서 C/C++의 volatile에 대해서 다시 설명하기는 힘들것 같고, Compaq Posix Thread Architect분의 인용문을 적겠습니다. "Compiler volatile semantics are not sufficient when sharing flag_ between threads, because the hardware, as well as the compiler, may reorder memory accesses arbitrarily, even with volatile. (Nor would a compiler implementation that issued memory barriers at each sequence point for volatile variables be sufficient, unless ALL data was volatile, which is impractical and unreasonably expansive.)" (원문 주소: http://tinyurl.com/5nk5s)

volatile은 일반적으로 portable한 코드를 위한 동기화 construct로서는
사용이 불가능합니다. 특히나 CPU가 2개 이상일 경우에는 예기치 못한 동작을 할 수 있고요...

인용한 글은 아래 주소에서 볼 수 있습니다.

http://tinyurl.com/49lhu

첫번째 인용문은 IBM 의 아키텍트이고 두번째 인용문은 HP의 Tru64 UNIX & VMS Thread Architect 의 글입니다.

"....
> - when the 'volatile' keyword must be used in
> multithreaded programming?

Never in PORTABLE threaded programs. The semantics of the C and
C++ "volatile" keyword are too loose, and insufficient, to have
any particular value with threads. You don't need it if you're
using portable synchronization (like a POSIX mutex or semaphore)
because the semantics of the synchronization object provide the
consistency you need between threads.

The only use for "volatile" is in certain non-portable
"optimizations" to synchronize at (possibly) lower cost in
certain specialized circumstances. That depends on knowing and
understanding the specific semantics of "volatile" under your
particular compiler, and what other machine-specific steps you
might need to take. (For example, using "memory barrier"
builtins or assembly code.)

In general, you're best sticking with POSIX synchronization, in
which case you've got no use at all for "volatile". That is,
unless you have some existing use for the feature having nothing
to do with threads, such as to allow access to a variable after
longjmp(), or in an asynchronous signal handler, or when
accessing hardware device registers.

---

The C language defines a series of "sequence points" in the "abstract
language model" at which variable values must be consistent with language
rules. An optimizer is allowed substantial leeway in reordering or
eliminating sequence points to minimize loads and stores or other
computation. EXCEPT that operations involving a "volatile" variable must
conform to the sequence points defined in the abstract model: there is no
leeway for optimization or other modifications. Thus, all changes
previously made must be visible at each sequence point, and no subsequent
modifications may be visible at that point. (In other words, as C99 points
out explicitly, if a compiler exactly implements the language abstract
semantics at all sequence points then "volatile" is redundant.)

On a multiprocessor (which C does not recognize), "sequence points" can only
be reasonably interpreted to refer to the view of memory from that
particular processor. (Otherwise the abstract model becomes too expensive
to be useful.) Therefore, volatile may say nothing at all about the
interaction between two threads running in parallel on a multiprocessor.

On a high-performance modern SMP system, memory transactions are effectively
pipelined. A memory barrier does not "flush to memory", but rather inserts
barriers against reordering of operations in the memory pipeline. For this
to have any meaning across processors there must be a critical sequence on
EACH end of a transaction that's protected by appropriate memory barriers.
This protocol has no possible meaning for an isolated volatile variable,
and therefore cannot be applied.

The protocol can only be employed to protect the relationship between two
items; e.g., "if I assert this flag then this data has been written" paired
with "if I can see the flag is asserted, then I know the data is valid".

That's how a mutex works. The mutex is a "flag" with builtin barriers
designed to enforce the visibility (and exclusion) contract with data
manipulations that occur while holding the mutex. Making the data volatile
contributes nothing to this protocol, but inhibits possibly valuable
compiler optimizations within the code that holds the mutex, reducing
program efficiency to no (positive) end.

If you have a way to generate inline barriers (or on a machine that doesn't
require barriers), and you wish to build your own low-level protocol that
doesn't rely on synchronization (e.g., a mutex), then your compiler might
require that you use volatile -- but this is unspecified by either ANSI C
or POSIX. (That is, ANSI C doesn't recognize parallelism and therefore
doesn't apply, while POSIX applies no specific additional semantics to
"volatile".) So IF you need volatile, your code is inherently nonportable.

A corollary is that if you wish to write portable code, you have no need for
volatile. (Or at least, if you think you do have a need, it won't help you
any.)

In your case, trying to share (for unsynchronized read) a "volatile"
counter... OK. Fine. The use of volatile, portably, doesn't help; but as
long as you're not doing anything but "ticking" the counter, (not a lot of
room for optimization) it probably won't hurt. IF your variable is of a
size and alignment that the hardware can modify atomically, and IF the
compiler chooses the right instructions (this may be more likely with
volatile, statistically, but again is by no means required by any
standard), then the worst that can happen is that you'll read a stale
value. (Potentially an extremely stale value, unless there's some
synchronization that ensures memory visibility between the threads at some
regular interval.) If the above conditions are NOT true, then you may read
"corrupted" values through word tearing and related effects.

If that's acceptable, you're probably OK... but volatile isn't helping you.

-----

http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

이 글이 충분한 설명이 되리라 생각합니다.
Scott Meyers 와 Andrei Alexandrescu가 쓴 글인데, 두 사람다 C/C++에서는 충분히 알려져있는 사람들이라 읽을 가치가 있을 것입니다. 특히 최근 자바의 concurrency에서는 매우 유명한 Doug Lea 등이 review 한 글입니다.

이 글을 작성하며 또 하나 쓸만한 자료를 찾았습니다.

http://tinyurl.com/boyse

한문장만 따왔습니다.

Quote:

> C/C++ volatile DOESN'T address any issues related to THREADING.

자바 진영내에서도 memory barrier, reordering을 설명하느라 무척이나 애먹었었습니다. 계속 SMP에서의 동기화를 잘 이해하지 못한 반론이 나와서요.. 논쟁이 계속 제자리를 맴돌자 포기하고, 정리한 내용은 프세에 멀티 CPU상에서의 쓰레딩을 주제로 글을 기고 하기도 하였습니다. 그런데 정작 그 논쟁이 끝나고 기사가 나간뒤에도, 한 자바 전문가 분이 제게 '누가 맞는거였는지'를 물어보더군요.. 더욱 더 많은 사람에게 정확한 사실이 알려지기 바랍니다.

p.s.
MS의 블로거들도 volatile이 뭔지 잘 모르는 상태로 지냈던 사람이 있나 봅니다...
http://blogs.msdn.com/brada/archive/2004/05/12/130935.aspx

--
Passion is like genius; a miracle.

익명 사용자의 이미지

volatile은 변수를 register가 아닌 메인 메모리에 할당하도록 하는 컴파일러 키워드지요.
thread에서 그 변수를 깨먹느냐 하는 문제는 아키텍쳐를 배워본 일 없는 사람들이 cache coherence같은문제에 대해 무지해서 그런 상황이 생기는데... 공부좀 하세요 해봐야 난 소프트웨언데 왜 하드웨어를 봐? 그런소리 하기때문에 앞으로도 계속 삽질하는 인구는 늘어나리라 봅니다.. 냠냠냠 그나저나 레지스터와 메인 메모리를 구분 못하는 사람도 있으려나 @.@

girneter의 이미지

Anonymous wrote:
volatile은 변수를 register가 아닌 메인 메모리에 할당하도록 하는 컴파일러 키워드지요.
thread에서 그 변수를 깨먹느냐 하는 문제는 아키텍쳐를 배워본 일 없는 사람들이 cache coherence같은문제에 대해 무지해서 그런 상황이 생기는데... 공부좀 하세요 해봐야 난 소프트웨언데 왜 하드웨어를 봐? 그런소리 하기때문에 앞으로도 계속 삽질하는 인구는 늘어나리라 봅니다.. 냠냠냠 그나저나 레지스터와 메인 메모리를 구분 못하는 사람도 있으려나 @.@

이사람 아주 제정신이 아니구만.
방학숙제로 레지스터와 메모리에 대해 공부했더니 뿌듯해서
자랑이라도 하고 싶은겐가?
우습다 못해 안타깝다.
위에 cinsk 님이나 pool007 이 volatile 에 대해 설명하신걸
읽어보긴 한건가?

개념없는 초딩들은 좋은 말로 할때 DC나 웃대가서 놀아라. 응?

doldori의 이미지

Anonymous wrote:
volatile은 변수를 register가 아닌 메인 메모리에 할당하도록 하는 컴파일러 키워드지요.

금시초문이군요. ^^;
volatile의 의미는
cinsk wrote:
volatile은 컴파일러가 알 수 없는 방법으로, 변경될 소지가 있는 것들에 volatile을 붙임으로써, 최적화?와 같은 작업이 일어나지 않고 코드 그대로 동작하게 강제하는 것입니다.

이 가장 잘 설명한 것입니다. 그런데 이의 구체적인 의미는 구현체 정의 사항으로
남겨두고 있기 때문에 volatile 개체가 보이는 구체적인 행동이 어떻다고는 말할
수 없는 것도 사실입니다.
Quote:
C99 6.7.3/6
[...] What constitutes an access to an object that has volatile-qualified
type is implementation-defined.

또한 C++에서 volatile은 C와 같은 의미를 갖되 "강제"가 아닌 "hint"라고 규정하여
구현체에게 더 많은 재량권을 부여하고 있습니다.

pool007의 이미지

Anonymous wrote:
volatile은 변수를 register가 아닌 메인 메모리에 할당하도록 하는 컴파일러 키워드지요.

컴파일러는 '메모리에 저장하고자 시도' 합니다. 하지만 '메인 메모리에 저장하는데 실패'합니다. MMU때문이죠.

Quote:
thread에서 그 변수를 깨먹느냐 하는 문제는 아키텍쳐를 배워본 일 없는 사람들이 cache coherence같은문제에 대해 무지해서 그런 상황이 생기는데... 공부좀 하세요 해봐야 난 소프트웨언데 왜 하드웨어를 봐? 그런소리 하기때문에 앞으로도 계속 삽질하는 인구는 늘어나리라 봅니다.. 냠냠냠 그나저나 레지스터와 메인 메모리를 구분 못하는 사람도 있으려나 @.@

cache coherency라고 착각하는게 가장 큰 문제입니다.
MMU에 의한 리오더링이 문제입니다.

앞서 글에서도 말씀드렸지만, 예전에 자바 진영에서
이 문제를 설명할 때 역시 링크를 아무리 해도 사람들이 읽어보지를 않습니다.
그냥 직관에 의해서 말하고, 그게 아니라고 설명해도 끝없이 빙빙돕니다..

정말 귀찮으면 두개만 읽어보세요.
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

읽고나서도 C/C++환경에서 멀티쓰레딩을 위해 volatile이 필요하다.. 라고 생각하면
뉴스그룹 comp.programming.threads에 포스팅하고 제게도 알려주세요.
여러 시스템의 아키텍트들이 많으니 토론 해볼 수 있을 것입니다..

--
Passion is like genius; a miracle.

pool007의 이미지

Anonymous wrote:
volatile은 변수를 register가 아닌 메인 메모리에 할당하도록 하는 컴파일러 키워드지요.

아니요. 절대로 아닙니다.

원하시는대로 volatile로 선언할 경우 데이터를 메모리에 저장하도록 하고 있는 언어는 C#과 JAVA가 있습니다. C/C++은 그렇지 않습니다. C 언어에 volatile이 있던게 적어도 80년대 중반이전이었던것 같은데, 그 때 SMP를 고려한 처리가 되고 있었을리가 없지 않나요.. 그 시절 volatile 의 목적은 하드웨어를 다루거나 TSR 프로그램들에서 쓰기 위함이었습니다.

스트라우스트럽 아저씨의 말에 따르면 C++ 은 쓰레드를 언어에 내장하여 쓰레드를 쓰기 편리하게 하고자하는 언어가 아닙니다. 그런 문제는 별도의 라이브러리가 해결해야하는 부분이고, 그런 라이브러리를 언어에 포함시킨다면 언어가 비대해진다는 문제가 있죠. C++의 경우엔 general purpose programming language를 지향하여, 쓰레드를 잘 쓰자라던가 그래프를 잘 그리자와 같은 문제를 해결하고자하기 보다는, 모든 문제를 잘풀기 위한 일반적인 프로그래밍 환경을 제공하고자하는 것입니다.

C나 C++에서 volatile이 SMP 상에서 돌아가는 멀티쓰레딩 프로그래밍의 thread-safety를 보장하지 못한다고 해서 그들 언어가 나쁘다는 것이 아닙니다... 실제로 JAVA에서는 volatile이 멀티쓰레딩에서의 safety를 지켜줄 수 있는 construct로서 volatile을 제공하지만, 하드웨어 접근을 위한 의미의 volatile은 없는 셈이죠..

다만 volatile이 멀티쓰레딩에서 쓰기 위함이라는 오해만 깨면 됩니다.

--
Passion is like genius; a miracle.

cinsk의 이미지

오해의 소지가 있어서 다시 글을 씁니다.

일단 C/C++은 multi threaded를 고려하지 않았으므로, volatile이 multi-threaded program에 대해서 concurrency를 보장해 주지 않습니다.
그럼 어떻게 하느냐? thread 기능을 제공해 주는 library에서 제공하는 lock/unlock 기능을 써야 합니다.

둘째, thread library의 유무와 상관없이, 자신의 코드가 다른 코드와 같이 동작해야 할 경우가 있습니다. 일단 이해를 돕기 위해, thread library를 쓰지 않았지만, 내가 작성한 C/C++ 코드가 assembler로 작성된 코드와 동작한다고 가정해 보기 바랍니다. 이 때 C/C++ compiler는 assembler로 작성된 코드에 대해 아는 바가 없으므로, 이 경우, volatile이 의미있을 수 있습니다. 즉, 최적화의 대상에서 제외하는 등의 강제성을 띠게 할 필요가 있습니다.

세째, 둘째의 경우가 thread library를 쓴 경우에 자연히 해결되느냐. 그렇지는 않습니다. 자신의 C/C++ 코드에 직접적으로 thread를 다루는 코드가 있던 없던 상관없이, volatile의 기능이 필요할 때가 존재합니다.

아마 몇 가지 오해가 있었던 것 같은데,

첫째, (제가 Java는 잘 모르지만) Java에서의 volatile은 그 의미가 C/C++에서 volatile과 상당히 다릅니다. 여기 참고

둘째, volatile keyword와 register keyword는 아무런 관련이 없습니다.
또 서로 상반되는 개념도 아닙니다.

세째, thread를 직접적으로 쓰는 C/C++ program을 만들때, concurrency를 위해서 thread library에서 제공하는 lock/unlock 등의 방법을 써야 하지, volatile을 쓰는 것은 아닙니다.

익명 사용자의 이미지

cinsk wrote:

둘째, volatile keyword와 register keyword는 아무런 관련이 없습니다.
또 서로 상반되는 개념도 아닙니다.

이에 대해 첨언하자면, volatile은 Type qualifier에 속하고 register는 storage-class specifier에 속합니다. 양립 가능하다는 뜻이지요.

volatile register int i;

역사적인 이유로 그 의미를 혼동하기 쉬운 이름이 붙어있는 키워드가 몇개 있는데, volatile과 register가 그중 하나입니다. 특히나, 지금의 register는 변수를 레지스터에 잡는 것과는 아무런 상관이 없고, 단지 '가능한 한 더 빠르게 접근할 수 있도록 노력하라'라는 권고에 지나지 않습니다. 물론 컴파일러가 그 '더 빠르게 접근할 수 있는 방법'중에 레지스터에 올리는 방법을 선택할 수도 있습니다만...

이래서 사람들이 좀 최신 책으로 공부를 해야 하는 것인데, ANSI C가 제정된 지 15년이 넘었는데도 구시대의 망령은 아직도 사람들 곁을 떠나질 않는군요...

익명 사용자의 이미지

이론적으로는 volatile이 멀티 쓰레드에서의 원자성을 보장해 주지 않습니다만, 많은 멀티쓰레드 코드에서 volatile을 습관적으로 사용하더군요. 그렇다면 충분조건은 되지 못해도 필요조건은 된다고 보는 편이 맞지 않을까요?

ixevexi의 이미지

Anonymous wrote:
이론적으로는 volatile이 멀티 쓰레드에서의 원자성을 보장해 주지 않습니다만, 많은 멀티쓰레드 코드에서 volatile을 습관적으로 사용하더군요. 그렇다면 충분조건은 되지 못해도 필요조건은 된다고 보는 편이 맞지 않을까요?

저도 일단은 이렇게 이해를 했는데요
즉 멀티쓰레드에서 lock/unlock개념과는 상관없이
volatile자체로써 의미를 가질 수 있다
(컴파일러 임의의 코드 생성이 아닌 사용자가 의도한 코드 그대로)
고 이해했습니다.

C++, 그리고 C++....
죽어도 C++

ㅡ,.ㅡ;;의 이미지

일반pc나 서버등에 작성하는 프로그램에는 volatile이 불필요할것같은데
좀다른 시스템에서는 의미가 있을꺼 같은데요..
그러니까.. 펌웨등에말이죠.. 특정 레지스터 혹은 메모리에
단순값을 대입했을때 그메모리의값은 그자체만으로도 어떤의미를 가지고 다른프로세싱에의해 동작하죠..
그런경우 최적화에의해 메모리에 값을대입하지못하면 문제가 생기죠.


----------------------------------------------------------------------------

valor의 이미지

저~ 위에 있는 volatile int *port라는 선언이 있는데요,

제 경험상 이렇게 써보면 전혀 volatile하게 *port 값을 access하지 못하는 것 같더군요.

정확하게 원하는 동작을 얻으려면

int *port로 그대로 두고,

(volatile int)sum+=(volatile int)*port;

이런 식으로 해야 제대로 동작하더군요. 앞의 것이나 뒤의 것 둘중의 하나는 빼도 되던지 안되던지 잘 기억이 안나네요 ^^;;;

volatile int *port는 말 그대로 port의 값을 volatile로 사용한다는 것이지 *port를 volatile로 쓴다는게 아니더군요...

girneter의 이미지

valor wrote:

int *port로 그대로 두고,

(volatile int)sum+=(volatile int)*port;

이런 식으로 해야 제대로 동작하더군요. 앞의 것이나 뒤의 것 둘중의 하나는 빼도 되던지 안되던지 잘 기억이 안나네요 ^^;;;

코드만 봐서는 sum 에는 volatile 을 붙일 이유가 전혀 없어보이는데
아닌가보죠? 흠...

개념없는 초딩들은 좋은 말로 할때 DC나 웃대가서 놀아라. 응?

pool007의 이미지

삭제..

--
Passion is like genius; a miracle.

pool007의 이미지

Anonymous wrote:
이론적으로는 volatile이 멀티 쓰레드에서의 원자성을 보장해 주지 않습니다만, 많은 멀티쓰레드 코드에서 volatile을 습관적으로 사용하더군요. 그렇다면 충분조건은 되지 못해도 필요조건은 된다고 보는 편이 맞지 않을까요?

아니요.

volatile이 뭘하는가하는 것은, 컴파일러에서의 optimization을 막는다라는 것입니다. 그럼 어떤 optimizaiton을 막는가라는 부분에 대해선, 제가 알기로는 volatile로 선언된 변수끼리의 reordering을 막는다고 알고 있습니다.

멀티 쓰레딩에서의 안정성을 위한 코딩에서, 변수간의 읽기/쓰기의 reordering을 막고 동기화를 해주며 변수 값을 메모리에 저장하는 construct가 있습니다. 뮤텍스라고요.. lock/unlock이면 충분히 되는 일입니다.

한번 생각해보세요.
멀티쓰레딩으로 소켓통신을 하는 서버를 작성할때 volatile이 필요할까요? 아니라고 봅니다.

--
Passion is like genius; a miracle.

doldori의 이미지

valor wrote:
volatile int *port는 말 그대로 port의 값을 volatile로 사용한다는 것이지 *port를 volatile로 쓴다는게 아니더군요...

그 반대 아닌가요? volatile은 const와 같은 qualifier입니다.
const int* port에서 const인 것은 *port이지 port가 아니지요.
마찬가지로 volatile int* port는 *port가 프로그램에게 알려지지 않은 방식으로
변경될 수 있다는 뜻입니다.
익명 사용자의 이미지

Quote:
main()
{
int *src = (int *)0x07;
*src;
}

subl %eax, %esp
movl $7, -4(%ebp)
leave
ret

main()
{
volatile int *src = (volatile int *)0x07;
*src;
}

movl $7, -4(%ebp)
movl -4(%ebp), %eax
movl (%eax), %eax

leave
ret

madman93의 이미지

좋은 내용이군요
많이 배워 갑니다.

---------------------------------------------데
리눅스가 싫다 우분투가 좋다
---------------------------------------------

---------------------------------------------
git init
git add .
git commit -am "project init"
---------------------------------------------

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.