volatile로 변수를 선언한다는 것은 언제든 그 값이 바뀔 수 있으므로 최적화를 하지 않겠다는 의미.
따라서 컴파일러는 volatile 선언된 변수에 대해서는 최적화를 수행 X volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.
(어떤 코어에서 a라는 변수를 건드렸을 때, 다른 코어가 알아채지 못함)
Volatile 이 실제 메모리에서 가져온다는 표현을 보게 되고, 캐시를 거치지 않는다고 착각하게 된다.
→ 컴파일러 최적화가 될 때 간혹가다가 변수를 레지스터에 올려놓고 그 레지스터 값을 바로 는 케이스가 있다. (착각하게되는 경위)
이는 레지스터메모리에 들어있는 값을 굳이 메모리에 들리지 않고 그대로 사용하는 것으로 최적화의 한 방법. 캐시메모리에 접근하지않고 물리메모리에 접근하는 것은 존재할수 없으며, 우리가 결정할 수 있는 사항이 아니다.
‘volatile은 최적화를 하지 않는다’ 외에 그 어떤 기능도 없다.
*(unsigned int *)0x8C0F = 0x8001 *(unsigned int *)0x8C0F = 0x8002; *(unsigned int *)0x8C0F = 0x8003; *(unsigned int *)0x8C0F = 0x8004; *(unsigned int *)0x8C0F = 0x8005;
(잘못된 하드웨어 제어 코드. 5번의 메모리 쓰기가 모두 같은 주소인 0x8C0F에 이뤄진다.)
이 경우 컴파일러는 속도향상을 위해 최종적으로 불필요한 쓰기를 제거해버리고 마지막 쓰기만 수행한다.
이러한 최적화는 문제가 없을 것 같지만, MMIO(Memmory-mapped I/O)처럼 메모리 주소에 연결된 하드웨어 레지스터에 값을 쓰는 프로그램이라면 이야기가 달라진다. 각각의 쓰기가 하드웨어에 특정 명령을 전달하는 것이므로, 주소가 같다는 이유만으로 중복되는 쓰기 명령을 없애버리면 하드웨어가 오동작하게된다.
이 경우 volatile을 선언해주면 모든쓰기를 수행해준다.
***(volatile unsigned int *)0x8C0F = 0x8001
*(volatile unsigned int *)0x8C0F = 0x8002;
*(volatile unsigned int *)0x8C0F = 0x8003;
*(volatile unsigned int *)0x8C0F = 0x8004;
*(volatile unsigned int *)0x8C0F = 0x8005;**
특정 메모리 주소에서 하드웨어 레지스터 값을 읽어오는 프로그램의 경우도 마찬가지다.