본문 바로가기
카테고리 없음

False Sharing은 왜 멀티코어 성능을 떨어뜨릴까

by by_merry 2026. 5. 30.

멀티코어 CPU 구조를 공부하다 보면 어느 순간부터는 “스레드를 늘렸는데 왜 오히려 느려지지?” 같은 상황을 자주 보게 된다. 나도 처음에는 멀티스레드 프로그램은 CPU 코어를 많이 사용할수록 거의 무조건 빨라지는 줄 알았다. 실제로 코어가 여러 개면 동시에 일을 처리할 수 있으니까 당연히 성능이 올라갈 것처럼 느껴졌기 때문이다. 그런데 Cache Coherency 구조와 멀티코어 캐시 동기화 문제를 조금씩 이해하다 보니까, 현대 CPU는 단순 계산보다 “코어끼리 데이터를 공유하는 비용” 때문에 훨씬 복잡한 병목이 발생할 수 있다는 걸 알게 됐다. 그리고 그중에서도 굉장히 유명한 성능 문제 중 하나가 바로 False Sharing이다. 처음 이름만 들었을 때는 뭔가 잘못된 공유 메모리 문제인가 싶었는데, 구조를 이해하고 나니까 왜 서버 엔지니어들이 멀티스레드 최적화에서 False Sharing을 굉장히 중요하게 보는지 조금 감이 오기 시작했다. 특히 인상 깊었던 건, 서로 완전히 다른 변수를 수정하고 있는데도 CPU 캐시라인(Cache Line) 때문에 성능이 크게 떨어질 수 있다는 점이었다. 나도 처음에는 “다른 변수인데 왜 서로 영향을 받지?”라는 생각이 들었는데, 실제로 현대 CPU는 메모리를 변수 단위가 아니라 캐시라인 단위로 관리하고 있었던 것이다.

 

CPU는 메모리를 변수 단위가 아니라 Cache Line 단위로 관리한다

현대 CPU는 RAM 속도가 CPU 계산 속도를 따라오지 못하기 때문에 캐시(Cache)를 굉장히 적극적으로 사용한다. 그리고 이 캐시는 보통 Cache Line이라는 단위로 동작한다. 쉽게 말하면 CPU는 메모리 데이터를 한 바이트씩 가져오는 게 아니라, 일정 크기 블록 단위로 한 번에 가져와 캐시에 저장하는 것이다. 보통 최신 CPU에서는 64바이트 정도 크기의 Cache Line을 많이 사용한다고 한다. 처음에는 나도 “어차피 변수 접근인데 왜 블록 단위로 가져오지?” 싶었는데, 실제로 프로그램은 주변 메모리를 연속적으로 접근하는 경우가 많기 때문에 이렇게 묶어서 가져오는 게 훨씬 효율적이라고 한다. 문제는 멀티코어 환경에서 발생한다. 예를 들어 Core A가 어떤 변수 A를 수정하고 있고, Core B가 완전히 다른 변수 B를 수정한다고 가정해보자. 변수 자체는 서로 다르지만 우연히 같은 Cache Line 안에 들어있을 수도 있다. 그러면 CPU 입장에서는 두 코어가 “같은 Cache Line”을 동시에 수정하는 상황처럼 보이게 된다. 나도 처음에는 “변수가 다르면 상관없는 거 아닌가?”라고 생각했는데, CPU 캐시는 변수 단위가 아니라 Cache Line 단위로 coherence(일관성)를 관리하기 때문에 이런 상황에서도 캐시 invalidation과 coherence traffic이 계속 발생할 수 있다고 한다. 결국 서로 전혀 관계없는 변수인데도 캐시라인을 공유하고 있다는 이유만으로 코어끼리 계속 충돌하는 상황이 생길 수 있는 것이다. 그리고 이런 현상을 바로 False Sharing이라고 부른다. 이름 그대로 “실제로는 공유하지 않는데 CPU는 공유 중이라고 착각하는 상황”인 셈이다.

False Sharing은 캐시 invalidation 폭발 때문에 성능을 크게 떨어뜨린다

False Sharing이 무서운 이유는 단순히 캐시 효율이 조금 나빠지는 수준이 아니기 때문이다. 문제는 Cache Coherency 프로토콜이 계속 동작하면서 코어 간 coherence traffic이 폭발적으로 증가할 수 있다는 점이었다. 예를 들어 Core A가 Cache Line 안의 변수 하나를 수정하면 다른 코어들이 가지고 있는 동일 Cache Line 캐시는 invalidation된다. 그런데 Core B도 같은 Cache Line 안 다른 변수를 수정하려고 하면 다시 coherence 과정이 반복된다. 결국 두 코어는 실제로는 서로 다른 데이터를 수정하고 있는데도 캐시라인 ownership을 계속 뺏고 빼앗기는 상황이 발생하게 되는 것이다. 나도 처음에는 “캐시 한번 다시 가져오는 게 그렇게 큰 문제인가?” 싶었는데, 최신 CPU는 워낙 빠르게 동작하기 때문에 이런 invalidation traffic 자체가 굉장히 큰 병목으로 이어질 수 있다고 한다. 특히 멀티스레드 서버나 병렬 연산 환경처럼 여러 코어가 동시에 작업하는 상황에서는 False Sharing 때문에 성능이 몇 배 이상 차이 날 수도 있다고 한다. 실제로 서버 최적화 코드에서는 변수 padding을 일부러 넣어서 서로 다른 스레드 데이터가 같은 Cache Line 안에 들어가지 않게 만드는 경우도 많다고 한다. 처음에는 나도 “메모리 낭비 아닌가?”라고 생각했는데, 최신 CPU에서는 약간의 메모리 낭비보다 캐시 coherence traffic 감소가 훨씬 더 중요할 수 있다는 걸 보고 꽤 충격이었다. 결국 현대 멀티코어 CPU는 단순 계산 성능 경쟁이 아니라, 코어끼리 캐시라인 충돌을 얼마나 줄일 수 있는가의 경쟁으로까지 발전하고 있었던 것이다.

그래서 현대 서버 최적화는 데이터 배치 구조까지 신경 쓰게 되었다

지금까지 공부했던 NUMA, Cache Coherency, Huge Page 같은 기술들을 보면 전부 공통된 방향이 있었다. 바로 CPU가 데이터를 기다리거나 불필요하게 이동하지 않게 만드는 것이다. False Sharing 역시 완전히 같은 흐름 위에 있다. 다만 이번에는 메모리 위치나 주소 변환이 아니라, “데이터가 캐시라인 안에 어떻게 배치되는가” 자체가 성능 문제가 되는 것이다. 특히 최신 서버 CPU는 코어 수가 계속 증가하고 있기 때문에 False Sharing 영향도 더 커지고 있다고 한다. 예를 들어 AI 서버나 데이터베이스 서버처럼 멀티스레드 작업이 많은 환경에서는 코어 간 coherence traffic 자체가 큰 병목이 될 수 있다고 한다. 나도 처음에는 CPU 성능 최적화라는 게 알고리즘이나 계산 속도 문제인 줄 알았는데, 실제로는 변수 배치와 캐시라인 구조까지 전부 고려해야 한다는 게 꽤 흥미로웠다. 특히 현대 시스템은 계산보다 데이터 이동과 캐시 동기화 비용이 훨씬 더 중요한 시대처럼 느껴졌다. 그래서 최신 서버 최적화에서는 단순 코드 로직뿐만 아니라, 메모리 layout과 cache-friendly 구조까지 굉장히 중요하게 다뤄진다고 한다. 결국 현대 멀티코어 CPU는 단순히 빠른 계산기 여러 개가 아니라, 엄청난 양의 캐시 동기화와 데이터 배치 최적화를 필요로 하는 굉장히 복잡한 시스템에 가까웠던 것이다. 이걸 공부하면서 가장 인상 깊었던 건 현대 컴퓨터 성능은 단순 계산 속도 경쟁이 아니라, 데이터를 얼마나 충돌 없이 효율적으로 배치하고 공유할 수 있는가의 경쟁으로 계속 진화하고 있다는 점이었다.

한 줄로 정리하면 False Sharing은 서로 다른 스레드가 실제로는 다른 변수를 수정하고 있음에도 같은 Cache Line을 공유하면서 캐시 invalidation과 coherence traffic이 반복되는 현상이며, 멀티코어 CPU 성능을 크게 떨어뜨리는 대표적인 병목 문제다.