[cpu의 사기 1] alignas
멀티쓰레드가 싱글쓰레드보다 더 느리다?
cpu의 코어들은 각각의 쓰레드 작업을 할때 코어마다 있는 캐시(L1, L2)에 변수를 저장하여 빠르게 작업을 처리하려 한다.
[L3는 각 코어가 공유하는 데이터를 저장함]
그런데 캐시에 변수를 기록할때 딱 그 변수크기만 읽어서 기록하지 않는다. 캐시라인 단위로 읽어 온다.
보통 cpu는 64byte씩 통째로 읽어들인다.
따라서 쓰레드가 int 변수 하나만 가져와 쓴다 하더라도 그 변수 위치부터 64byte 만큼 한꺼번에 읽어서 가져온다.
여기서 어떤 상황에서 false sharing 이라는 것이 발생하고 성능저하를 일으킨다. 즉 하드웨어로 인한 성능저하다.
그 어떤 상황의 예)
int a=0;
int b=0;
void thread_1()
{
for(int i=0; i<1000; ++i)
++a;
}
void thread_2()
{
for(int i=0; i<1000; ++i)
++b;
}
core1 에서 thread_1을 실행, core2 에서 thread_2를 실행한다고 했을때, 각 코어의 캐시에는 변수가 다음과 같이 기록될 수 있다. (아닐 수도 있음)
여기서 변수 b가 각 코어의 캐시에 동일하게 공유되어있다. cpu는 cpu끼리 공유되고 있는 변수가 있을 시 동기화를 위한 작업을 수행한다. (MOESI 프로토콜)
간단히 설명하면 공유되고있는 데이터의 변화를 각자의 캐시에 적용하면 서로 변화를 모른다. 그래서 어떤 한 코어에서 데이터 변경 시 공유 데이터가 있으면 다른 코어의 캐시를 invalid시킨다.
즉, Core1에서 데이터를 업데이트 시, b라는 변수가 Core2와 공유되고있기 때문에 Core2의 캐시라인을 invalid한다. ( Core1은 b를 고치지 않지만 어쨋든 캐시라인에 공유되고 있기때문에 착각하여 동기화를 진행한다.)
그러면 후에 Core2에서 작업하려할 시, 캐시라인이 invalid되었기 때문에 Core1의 캐시로부터 데이터를 다시 가져와야 해서 더 많은 시간이 걸린다.
이렇게 서로 다른 데이터를 사용하지만 캐시라인 공유로 인해 하드웨어상 동기화 작업으로 성능저하가 나타난다.
이를 해결하기위해 있는 것이 alignas
alignas(64) 키워드를 사용하여 변수 사이의 주소를 물리적으로 떨어뜨린다.
alignas(64) int a=0;
alignas(64) int b=0;
void thread_1()
{
for(int i=0; i<1000; ++i)
++a;
}
void thread_2()
{
for(int i=0; i<1000; ++i)
++b;
}
+cpu캐시의 크기를 얻어오는 매크로
std::hardware_destructive_interference_size
*공부한 블로그 b
[05. 멀티쓰레드] 멀티쓰레드 2 (Lock 의 사용과 문제점 - 성능저하) (tistory.com)
false sharing이란? in 멀티 쓰레드(About Memory alignment) (tistory.com)