![viewimage.php?id=3dafdf21f7d335ab67b1d1&no=29bcc427b38577a16fb3dab004c86b6fb8c469a51a456a5787032fed780bd3a41127dc7a693301829f6ec5a1a9452a0743467248a29d5277fb608ab9f5]()
volatile int var = 1; for( int i = 0; i < loop_count; ++i ) var += var;
N 번 iteration 하는 for 문 안에서 volatile 로 선언된 변수의 var += var 를 수행한 결과 그래프. 역시 내가 만든 benchmark 환경은 깔끔한 그래프가 나온다. 히힣.
추세선의 수식이 clocks( loop_count ) = 5.8 * loop_count - 68 의 형태로 계산되고 있다. -68 이 나오는 이유는 rdtsc 명령을 수행하면서 파이프라인과 스칼라에 묻힌 것. 무시하면 된다.
0 에서 시작하는 기본 for 루프는 시작값과 반복조건 모두 상수인 경우는 3개의 명령으로 구성되지만 지금은 loop_count 가 변수기 때문에 내부적으로 4개 명령어 조합으로 이루어진다.
여기서 volatile 변수를 다루므로 값을 가지고 오고 다시 저장해야하기 때문에 load, add, store 가 추가되어 7개 정도의 명령을 루프에서 수행하게 될것이다.
disassemble 해보진 않았지만 대략 다음과 유사한 구조겠지.
loop_top: 1. cmp iterator, loop_count // 2. je loop_end // for 는 진입 제어문
3. mov eax, [&var] // load var 4. add eax, eax // adder 5. mov [&var], eax // store var
6. inc iterator 7. jmp loop_top loop_end:
여기서 1 ~ 2 사이에는 cmp 결과가 write back 되는걸 기다려야 하니까 data dependency 존재. 3 ~ 4, 4 ~ 5 사이에도 dependency 존재. 6 ~ 1 사이에 dependency가 걸려 있게 된다. ( 어차피 jmp 에 의해 초기화 되지만 )
즉 병렬화 가능한 부분은 2 ~ 3, 5 ~ 6, 6 ~ 7 사이밖에 없다. 모두 최적화 된다면 단일 파이프라인 기준 7 - 3 = 4 클럭이 소요되어야 하지만, cache expire 에 의해 var 값을 새로 읽어오는 과정은 루프에 다른 명령들이 충분히 있으니까 묻히겠고 jmp(7.), cmp(1.), je(2.), 도합 세 개의 명령에 부가적인 작업이 발생했고 4.8 클럭이 소요되었을 것이라 생각된다. ( 대략 1.6 * 3 )
4 + ( 4.8 - 3 ) = 5.8
|
volatile 이 아니었다면 3~4 clock 이 소요될 루프였다는 거쥬~
volatile 아니면 레지스터에만 담아뒀다가 다 끝나고 메모리에 쓰기 하는겨?
ㅇㅇ
그리고 loop_count 가 상수면 좀 더 빨라지겠쥬~ 4개가 아닌 3개 짜리 명령어 루프가 될테니
var를 다른데서 접근 가능하다 라고 한다면 volatile이든 아니든 메모리에 항상 써야 되는거 아닌감 최적화 기준같은걸 몰라서
쓰레드 하나 더 둬서 var 몇 이상일때 쎅쓰를 하게 한다던지 하는 경우 말여
rdtsc 두 번 부르는데 18 클럭 비용이 발생하니까 결과적으로 68 클럭 + 18 클럭 = 86 클럭 이하 비용의 명령어들은 한 번 돌려서 속도 비교가 불가능하다는것.
volatile 을 선언하는 이유가 무조건 최적화 하는걸 막기 위해서니께유.
3의 배수로 잡히니까 87클럭이겠군.
제온에서 테스트 할땐 93 이후부터 측정가능이던걸로 기억.
volatile 안썼을때의 결과는 경우에따라서 변할수 있다는거넹 ㅇㅋ
루즈셔플 말대로 멀티쓰레딩 문제를 풀려면 mutex 같은걸로 lock을 걸고 접근해야쥬.
화장실 문 안잠그고 앉아있으면 봉변당하는 수가 있음.
레이스컨디션이야 뭐... 그냥 멀티스레딩 경우에도 volatile 걸면 레지스터에서만 꼼지락거리는건지 궁금해씀 그러면 안되는거니깐
그러니까 컴파일러 정책은 지역 변수에 좀더 과격한 최적화를 걸 가능성이 높은거쥬. 쓰레드는 독립 스택공간을 가지니까. 지역변수가 아닌 경우엔 레지스터 만의 최적화는 걸지 않게 되고, 쓰기 접근이 발생한 page 의 cache 붕괴로 인해서 정상적인 접근은 됩니다유~
다만, 진짜 race 컨디션은 레지스터 내부 연산 문제가 아닌 memory 억세스에서 발생한다는거쥬.
뭐, 컴파일러에 따라서 전역 변수라 하더라도 volatile 로 처리 안하면 인터럽트에 의한 변경이든 다른 쓰레드에 의한 변경이든 냠냠 흘려버리는 녀석들이 있긴 하죠.
음 근데 말이 이상하다 volatile 걸면 레지스터에서만 꼼지락 거린다??? volatile 을 걸어야 레지스터에서 꼼지락거리지 않는건데?
아 그러넹 반대로 말해버림
ㅇㅇ 좋은 하루~
나도 뭔가 말을 이상하게 한것 같다. 진짜 race 컨디션은 지역변수가 아닌 전역이나 공유 memory 억세스에서 발생한다는거쥬. 라고 하고 싶었는데.
졸린규 퓨 ㅠㅠ 출근해야지.