내용 보기
작성자
아롱이 (IP : 172.17.0.1)
날짜
2020-07-22 16:54
제목
[병렬프로그래밍] 거짓 공유 (false sharing)
캐싱의 기본은 지역성에 근거하는데요, 이는 프로그래밍단의 최적화에서도 유명한 80-20법칙과도 일맥상통하는 이야기죠. 지역성(locality)은 아래 추정에 근거합니다.
이는 코드 실행시 스택 처리를 통해 얻게 되는 장점과 유사합니다. 단일 코어가 아닌 멀티 코어 CPU는 데이터를 읽어올때, 캐시 라인 (cache line)이란 단위로 읽어옵니다. 캐시 라인이라 함은 지역성에 근거해 인접한 데이터를 미리 읽어옴으로써 속도향상을 노리는 것이지요. 하지만 이는 장점이자 독이 되기도 합니다. 멀티코어에서는 A스레드와 B스레드에서 인접 메모리를 접근할때, 캐시에 있던 내용을 메모리에 반영하려 시도합니다. 인접 메모리를 읽고 있는 상태이기에 병행 수행시 데이터의 유효성을 조금이라도 높이기 위해 메모리에 반영하는 과정에서 속도 저하가 발생하는 것이죠. 실제로 인접메모리일뿐 동시 접근이 일어나지 않는 코드라고 할지언정, 해당 코드가 어떻게 작성되었는지는 중요치 않습니다. 캐시 라인은 코드의 작성 여부까지 판단하고 동작하지 않기 때문에 (그렇게 할 수 가 없기에), 인접 메모리 접근만으로도 성능 손해를 보면서라도 데이터의 유효성을 높이고자 하는 판단을 내릴 수 밖에 없습니다. convoying (무분별한 lock의 사용으로 멀티 스레드를 활용하지 못하고, 한개 스레드 동작시 다른 스레드들은 그 스레드가 unlock 할때 까지 대기 해야만 하는 상황) 보다야 낫겠지만, 멀티 코어가 일반화 되면서 이를 얼마나 잘 활용하는가가 화두가 되고 있는 이 시점에 메모리 거짓 공유로 인한 속도 저하는 반드시 염두에 두어야 하는 이슈입니다. 멀티코어 프로그래밍에서는, 자주 읽히는 데이터가 인접해 있다면, cache line 크기만큼의 간격을 두도록 하는 것이 좋습니다. 패딩(padding)을 이용해서 메모리를 손해보더라도 속도에서 이득을 보라는 얘기죠. 옛말에 “메모리 공간을 팔아 속도를 산다”는 말 처럼, 메모리와 속도는 반비례 그래프와 같다는 생각이 다시 한번 드네요. 다른 프로세서에서 사용되고 있는 복수의 동시실행 태스크가 같은 캐시라인에 배치되어 있는 변수에 쓰기를 하면 거짓 공유가 발생한다. 하나의 태스크가 어떤 변수에 쓰기를 하면 양쪽 변수의 캐시라인이 무효화된다. 캐시라인이 무효화 될 때마다 재 로딩이 필요하게 된다. 즉 거짓 공유가 발생하면 애플리케이션의 성능이 저하할 위험이 크다.
long count1 = 0L; long count2 = 0L; concurrency::parallel_invoke( [&count1] { for(int i = 0; i < 100000000; ++i) ++count1; }, [&count2] { for(int i = 0; i < 100000000; ++i) ++count2; } ); long count = count1 + count2; 위의 코드는 태스크에 공유 변수를 사용하지 않기 위해 count1, count2로 변수를 나누어서 사용하고 있다. 그래서 일견 보기에는 서로 독립적으로 실행될 것이라고 생각할 수 있다. 그러나 위에 설명했듯이 count1와 count2는 서로 같은 캐시라인에 배치될 확률이 높아서 결과적으로 거짓 공유가 발생한다.
이 문제를 해결하기 위해서는 아래처럼 메모리 정렬에 의해 서로 다른 캐시라인에 배치되도록 한다. __declspec(align(64)) long count1 = 0L; __declspec(align(64)) long count2 = 0L; concurrency::parallel_invoke( [&count1] { for(int i = 0; i < 100000000; ++i) ++count1; }, [&count2] { for(int i = 0; i < 100000000; ++i) ++count2; } ); long count = count1 + count2; 핵심은 __declspec(align(64))으로 메모리 캐시 사이즈가 64바이트 이하인 경우 같은 캐시라인을 사용하지 않도록 해준다.
__declspec 설명 |
출처1
https://elky84.github.io/2010/08/19/false_sharing
출처2
https://jacking.tistory.com/m/1175