한양과학기술고등학교

에듀테크 프로젝트 대시보드

캐시 일관성 (MESI 프로토콜)

캐시 일관성 — MESI 프로토콜

CPU 코어마다 자기만의 캐시가 있다. 코어 1이 캐시에 들고 있는 x 값을 코어 2가 바꾸면? 두 캐시가 서로 다른 값을 믿게 되는 불일치(incoherence)가 생긴다. 하드웨어는 MESI라는 상태 기계로 이 문제를 막는다.

1. 왜 문제가 되는가

메인 메모리는 느리다(수백 사이클). 그래서 각 코어는 자주 쓰는 데이터를 자기 L1/L2 캐시에 복사해 둔다. 코어가 여러 개면 같은 메모리 한 칸이 여러 캐시에 동시에 복사될 수 있다.

// x = 0 에서 시작. 코어 A, 코어 B 모두 캐시에 x를 들고 있음
코어 A:  x = 42        → A의 캐시에만 42 기록 (write-back)
코어 B:  print(x)      → B의 캐시에는 아직 0 ... 옛날 값을 읽음!

이런 일이 생기면 락(뮤텍스)을 아무리 잘 걸어도 소용없다. 그래서 캐시들끼리 공용 버스로 서로를 감시(bus snooping)하며, 한 캐시가 값을 바꾸면 나머지 캐시의 복사본을 즉시 무효화한다.

2. 네 가지 상태: M · E · S · I

상태뜻메모리와 일치?다른 캐시에도 있나?
M Modified내가 고쳤고, 나만 최신본을 가짐❌ 메모리가 낡음 (내가 나중에 써줘야 함)없음 (나만 유효)
E Exclusive나만 가지고 있지만 아직 안 고침✅ 일치없음
S Shared여러 캐시가 같은 값을 읽기용으로 공유✅ 일치있을 수 있음
I Invalid이 줄은 쓰레기 — 다시 읽어와야 함——
E 상태의 이점: "나만 가지고 있다"가 확실하면, 그 값을 고칠 때 다른 캐시에 알릴 필요가 없다. 그냥 조용히 E → M으로 바꾸면 끝 — 버스 트래픽 0. 싱글 스레드 프로그램이 멀티코어 CPU에서도 빠른 이유 중 하나다.

3. 직접 굴려 보기

코어 3개가 하나의 변수 x(메모리 초기값 0)를 공유한다. 각 코어의 읽기 / 쓰기 버튼을 눌러 보며 캐시 상태(M·E·S·I)와 버스 메시지가 어떻게 흐르는지 관찰하자. 쓰기는 그 코어가 x를 1 증가시킨다.

Main Memory   x = 0
● 최신

4. 상태 전이 규칙

M E I S 쓰기(로컬) 남이 읽음 읽기 미스(나만) 읽기 미스(공유) 쓰기(무효화 방송) 남이 씀 남이 씀 → 무효화
지금 상태내 코어가 읽음내 코어가 씀버스에서 "남이 읽음"버스에서 "남이 씀"
MM (히트, 조용)M (히트, 조용)메모리에 flush → S메모리에 flush → I
EE (히트, 조용)E → M (조용!)→ S→ I
SS (히트)무효화 방송 → MS 유지→ I
I버스에 읽기 요청 → 다른 캐시 있으면 S, 없으면 E버스에 쓰기 요청(무효화) → M——

5. 개발자에게 주는 교훈

거짓 공유(False Sharing)

캐시는 바이트 단위가 아니라 캐시 라인(보통 64바이트) 단위로 움직인다. 서로 다른 스레드가 다른 변수를 고쳐도, 그 두 변수가 같은 64바이트 라인에 들어 있으면 매번 서로의 라인을 무효화시켜 성능이 급락한다.

// 나쁜 예: counter[0]은 스레드0, counter[1]은 스레드1이 사용
long counter[2];              // 8+8=16바이트 → 같은 캐시 라인!

// 해결: 라인 크기만큼 패딩해서 서로 다른 라인에 배치
struct { long v; char pad[56]; } counter[2];
기억할 것: 원자적 연산(atomic)이나 락도 결국 이 무효화 트래픽을 유발한다. 여러 코어가 같은 변수를 자주 쓰면 아무리 잘 짜도 느리다 — 코어별로 따로 세고 나중에 합치는 설계가 빠르다.