멀티코어 CPU와 캐시 구조를 공부하다 보면 생각보다 이상한 상황들이 꽤 많이 등장한다. 그중 하나가 바로 False Sharing이다. 처음 이름만 들었을 때는 솔직히 무슨 의미인지 전혀 감이 안 왔다. “Sharing이면 공유라는 건데, 왜 False라는 말이 붙지?” 나도 처음에는 그냥 메모리를 잘못 공유하는 문제 정도로만 생각했다. 그런데 실제 구조를 이해하고 나니까 이게 단순한 버그가 아니라, 멀티코어 CPU에서 성능을 엄청 떨어뜨릴 수도 있는 꽤 중요한 문제라는 걸 알게 됐다.
특히 인상 깊었던 건, 코드 자체는 멀쩡하게 동작하는데도 성능만 갑자기 심하게 떨어질 수 있다는 점이었다. 즉, 프로그램은 정상인데 CPU 내부에서는 코어끼리 계속 싸우고 있는 상황이 생기는 것이다.

멀티코어 CPU는 서로 캐시를 계속 동기화한다
현대 CPU는 각 코어마다 자신만의 캐시(Cache)를 가지고 있다. 이유는 간단하다. RAM까지 직접 접근하면 너무 느리기 때문이다. 그래서 자주 사용하는 데이터를 각 코어 가까이에 저장해두고 최대한 빠르게 처리하려고 한다.
문제는 여러 코어가 동시에 같은 메모리 영역을 다룰 때 발생한다. 예를 들어 코어 A가 어떤 데이터를 수정했다면, 다른 코어들도 “기존 캐시 값이 더 이상 최신이 아니다”라는 걸 알아야 한다. 그래서 CPU는 코어끼리 캐시 상태를 계속 동기화한다.
처음에는 이 구조를 보고 “당연한 거 아닌가?” 싶었다. 여러 코어가 동시에 움직이는데 서로 데이터가 다르면 큰일 나니까 말이다. 그런데 문제는 CPU가 데이터를 개별 변수 단위로 관리하지 않는다는 점이었다.
CPU는 보통 Cache Line이라는 단위로 데이터를 관리한다. 쉽게 말하면 메모리를 작은 묶음 단위로 가져오는 것이다. 예를 들어 변수 하나만 수정해도, 실제로는 그 주변 데이터까지 포함된 Cache Line 전체가 영향을 받을 수 있다.
나도 처음 이 개념을 이해했을 때 꽤 의외였다. 분명 서로 다른 변수인데도, 물리적으로 가까이 붙어 있다는 이유만으로 같은 Cache Line 안에 들어갈 수 있다는 점이 신기했다.
False Sharing은 서로 다른 작업인데도 캐시 충돌이 발생하는 현상이다
False Sharing이 발생하는 대표적인 상황은 이렇다. 코어 A와 코어 B가 서로 완전히 다른 변수를 수정하고 있다. 개발자 입장에서는 “각자 다른 변수 쓰니까 문제 없겠네”라고 생각하기 쉽다.
그런데 만약 이 변수들이 우연히 같은 Cache Line 안에 들어 있다면 이야기가 달라진다. 코어 A가 변수 하나를 수정하면 CPU는 해당 Cache Line이 변경됐다고 판단한다. 그러면 코어 B가 가지고 있던 캐시도 무효화된다.
문제는 코어 B 입장에서는 자신이 사용하는 변수는 전혀 안 바뀌었는데도, 같은 Cache Line 안에 있다는 이유만으로 계속 캐시를 다시 가져와야 한다는 점이다.
즉, 실제로 데이터를 공유하는 것도 아닌데 CPU 입장에서는 마치 공유 충돌처럼 보이는 상황이 생기는 것이다. 그래서 이름도 False Sharing이다.
나도 처음에는 “설마 이런 걸로 성능 차이가 그렇게 커질까?” 싶었는데, 실제 벤치마크를 보니까 멀티스레드 성능이 꽤 심하게 떨어지는 경우도 많았다. 특히 반복적으로 값을 수정하는 작업에서는 캐시 무효화(Cache Invalidation)가 계속 발생하면서 CPU 코어끼리 엄청 자주 충돌하게 된다고 한다.
그걸 보고 처음으로 “멀티코어 성능은 단순히 코어 숫자만 늘린다고 해결되는 문제가 아니구나”라는 걸 실감하게 됐다.
그래서 고성능 프로그램은 메모리 배치까지 신경 쓴다
False Sharing이 흥미로운 이유는 프로그램 자체는 완전히 정상 동작한다는 점이다. 에러도 안 나고 결과도 맞다. 그런데 CPU 내부에서는 캐시 충돌 때문에 성능만 계속 손해 보고 있는 상황이 발생하는 것이다.
그래서 고성능 서버 프로그램이나 게임 엔진 같은 곳에서는 변수 배치까지 신경 쓰는 경우가 많다. 일부러 변수 사이에 패딩(Padding)을 넣어서 서로 다른 Cache Line을 사용하게 만들기도 한다.
나도 예전에는 “메모리 배치 같은 건 컴파일러가 알아서 하지 않을까?”라고 생각했는데, 실제로는 성능 최적화 단계에서는 개발자가 직접 메모리 구조를 조정하는 경우도 꽤 많다는 걸 알고 놀랐던 기억이 있다.
특히 멀티코어 시대에는 CPU 계산 속도 자체보다, 캐시 충돌과 메모리 접근을 얼마나 효율적으로 줄이느냐가 더 중요해지는 경우도 많다고 한다.
이걸 공부하면서 느낀 건 현대 CPU 구조는 단순히 “빠른 계산기”가 아니라는 점이었다. 내부에서는 수많은 코어와 캐시가 계속 데이터를 맞추고 동기화하면서 엄청 복잡하게 움직이고 있었던 것이다.
한 줄로 정리하면 False Sharing은 서로 다른 스레드가 실제로는 다른 데이터를 사용함에도 같은 Cache Line을 공유하면서 캐시 무효화가 반복되어 멀티코어 성능이 크게 떨어지는 현상이다.