----
Memory model
좀 이른 감이 있지만 여기서 memory model (memory consistency) 에 대해 이야기
해봅시다. 예전의 in-order CPU들은 instruction 이 들어오는 순서대로 (이를
program order라고 합니다. C코드가 아닌 어셈코드에서 instruction이 배열된 순
서를 말하죠. 컴파일러가 최적화후에 생성한 코드임에 주의하세요) 메모리
read/write를 수행했습니다. 이를 sequential consistency라고 합니다. 하지만
최근 CPU들은 성능을 위해서 memory reordering을 합니다. 즉 read/write가 마구
뒤섞을수가 있습니다. 이에 따라 각 아키텍쳐는 (relaxed) memory consistency를
가지고 있고, 과거의 sequential consistency는 옛말이 되어버리고 말았습니다.
예를들어 x86은 processor order라고 하는 consistency를 정의하고 있습니다. 이
제 그 위에서 각 언어들 Java, C, C++ 역시 자신만의 memory model을 정의하고
있습니다. 그 바탕에는 memory consistency가 있습니다. 이에 따라 과거의
sequential consistency에 기대던 일부 low-level 알고리즘들은 깨어지게 됩니
다. 앞서 살펴본 Dekker's algorithm이 그런 경우입니다. 이 알고리즘은
sequential consistency를 가정하고 있기때문에 memory reordering을 하는 많은
현대 CPU에서는 깨어지고 맙니다. Intel이 가진 processor order역시 sequential
보다 완화된 consistency이고 이때문에 이 알고리즘은 작동하지 않습니다. 여기
에서는 실제로 Dekker's algorithm이 깨어짐을 보여주고 있네요. 따라서 이를 수
정하기 위해서는 memory barrier를 쳐야합니다.
memory model에 큰 영향을 받는 코드는 주로 low-level의 코드들입니다. 대표적
으로 spinlock과 같은 lock들이 있습니다. 당연히 기존의 이러한 코드를 지켜주
기 위해서 기본적으로 spinlock등은 memory fence를 겸하게 됩니다. x86의 lock
prefix나 xchg 명령같은 경우가 그렇습니다. 따라서 대부분의 코드들이 무리없이
동작할수 있습니다. 물론 앞서의 Dekker's algorithm같은 예외가 있습니다. 그외
에도 또한 뒤에서 살펴볼 lock-free code같은 경우가 memory model에 아주 민감
한 경우라고 할수 있습니다.
따라서 sequential consistency에 의존하는 그러한 코드들은 memory fence를 쳐
서 지켜줘야합니다. Intel에는 3가지 타입의 fence instruction이 있습니다.
read/write모두 넘지 못하는 mfence (memory fence), read가 넘지 못하는
lfence(load fence), write가 넘지 못하는 sfence(store fence)이죠. TODO...
TAS 와 T&TAS , MCS, 역시 TODO...
물론 그외에도 컴파일러 역시 loop invariant등을 뽑아내는 최적화를 할수 있기
때문에 이를 방지하기 위해서 volatile키워드를 넣어주어야 합니다. 물론 C/C++
의 volatile은 컴파일러 최적화만을 방지하기 때문에 memory fence역시 직접 쳐
줘야합니다. C#이나 Java 의 volatile, 또는 C++0x의 atomic variable은 memory
fence역할까지 하지요.