False Sharing이란? 멀티코어 성능을 갉아먹는 캐시 라인 문제

False Sharing의 정의

False Sharing(거짓 공유)은 멀티코어 환경에서 서로 다른 스레드가 각기 다른 변수를 다루고 있음에도, 그 변수들이 같은 캐시 라인(cache line)에 위치해 있어서 불필요한 캐시 무효화가 반복적으로 발생하는 현상을 말합니다. CPU는 메모리를 바이트 단위가 아니라 캐시 라인 단위(보통 64바이트)로 읽고 쓰기 때문에 생기는 문제입니다.

예를 들어 스레드 A가 변수 x를, 스레드 B가 변수 y를 각각 수정한다고 해봅시다. 논리적으로는 두 변수가 전혀 관련이 없지만, x와 y가 메모리상 같은 캐시 라인 안에 나란히 존재한다면 이야기가 달라집니다. A가 x를 수정하는 순간 해당 캐시 라인 전체가 ‘변경됨’ 상태가 되고, B의 코어에 있던 캐시 라인 사본은 무효화됩니다. B가 y를 읽거나 쓰려는 순간 다시 메인 메모리(혹은 다른 코어의 캐시)에서 라인을 가져와야 하죠. 이 과정이 반복되면서 실제로는 데이터 경쟁(race condition)이 없는데도 성능이 크게 떨어집니다.

동작 원리와 예시

비유하자면, 두 사람이 각자 다른 서랍에 물건을 넣으려는데 그 서랍들이 하나의 큰 상자 안에 붙어 있는 상황과 비슷합니다. 한 사람이 상자를 열어 자기 서랍을 만질 때마다 상자 전체가 흔들려서, 다른 사람도 자기 서랍이 멀쩡한지 매번 다시 확인해야 하는 것이죠. 서랍(변수)은 다르지만 상자(캐시 라인)를 공유하기 때문에 발생하는 비효율입니다.

코드로 보면 다음과 같은 구조에서 False Sharing이 흔히 발생합니다.

struct Counter { int a; int b; }

여기서 스레드1이 a를 계속 증가시키고 스레드2가 b를 계속 증가시키면, a와 b가 같은 캐시 라인에 있을 가능성이 높아 심각한 성능 저하가 발생합니다. 이를 해결하려면 변수 사이에 패딩(padding)을 추가해 서로 다른 캐시 라인에 배치하거나, C++17의 std::hardware_destructive_interference_size와 같은 정렬 기능을 활용해 구조체를 캐시 라인 크기에 맞춰 정렬시킵니다.

실무에서 왜 중요한가

False Sharing은 컴파일 에러나 런타임 에러를 일으키지 않기 때문에 발견하기 어렵습니다. 로직은 완벽히 정상 동작하지만 멀티코어 환경에서 기대만큼 성능이 나오지 않는 원인 중 하나로 자주 지목됩니다. 특히 다음과 같은 상황에서 주의가 필요합니다.

  • 여러 스레드가 각자의 카운터나 통계값을 배열 형태로 관리할 때
  • 스레드별 작업 큐나 상태 플래그를 인접한 메모리에 배치할 때
  • 고성능이 요구되는 서버, 게임 엔진, 실시간 시스템의 락프리(lock-free) 자료구조 설계 시

이런 문제를 예방하기 위해 실무에서는 자주 접근되는 변수를 캐시 라인 크기(대개 64바이트) 단위로 패딩하거나, perf, Intel VTune 같은 프로파일링 도구로 캐시 미스율을 분석해 병목 지점을 찾아냅니다. 멀티스레드 성능 튜닝을 한다면 락 경합뿐 아니라 이런 하드웨어 수준의 캐시 동작까지 함께 고려하는 것이 중요합니다.

댓글 남기기