본문으로 건너뛰기
Embedded Performance Engineering · 38/57

Cache Coherency 프로토콜 — MESI·MOESI·Snoop·Directory

· Hawk · 8분 읽기

#한 줄 요약

“Cache coherency는 여러 cache가 같은 line에 대해 일관된 view를 갖도록 hardware가 자동으로 관리하는 mechanism입니다.”

#어떤 문제를 푸는가

각 CPU 코어는 자기 L1 cache를 가지고 있습니다. 한 코어가 변수에 write를 하면 그 값은 자기 L1에만 들어가고, 다른 코어의 L1은 옛 값을 그대로 가지고 있게 됩니다. 별다른 처리가 없다면 두 코어는 서로 다른 값을 보게 되어 모든 multi-threaded 코드가 깨집니다.

Cache coherence protocol은 이 문제를 hardware 차원에서 자동으로 해결합니다. 한 코어가 write하면 다른 코어의 cache line을 invalidate하거나 update해서 모두가 같은 값을 보도록 만듭니다.

이 글에서는 MESI 4-state 프로토콜과 ARM의 MOESI 확장, snoop-based와 directory-based 두 가지 구현 방식, 그리고 PMU로 coherency overhead를 측정하는 방법을 살펴봅니다.

#MESI 4-State

State의미다른 cacheMemory
M (Modified)이 cache만 valid, dirtyInvalidstale
E (Exclusive)이 cache만 valid, cleanInvalidlatest
S (Shared)여러 cache valid, clean가능 (S)latest
I (Invalid)무효

각 cache line이 이 네 상태 중 하나를 가지며, read와 write에 따라 상태가 전이됩니다. 그림으로 보면 어떤 동작이 어떤 전이를 일으키는지 한눈에 보입니다.

MESI 상태 전이 — local read/write(초록)와 remote read/write(빨강)

#MESI 상태 전이

Read miss:
- 다른 cache에 M? → flush + → I, this → S, memory updated
- 다른 cache에 E? → both → S
- 다른 cache에 S? → this → S
- 어떤 cache에도 없음? → memory에서 → E
Write to S:
- 다른 cache S → Invalidate broadcast → I
- this → M
Write to M: 그냥 cache에 (이미 exclusive)
Write to I (write miss):
- RFO (Read For Ownership) → 다른 cache invalidate
- this → M, memory not updated

RFO는 write 직전에 발생하는 read입니다. Write할 cache line을 먼저 자기 cache로 가져오면서 다른 cache의 동일 line을 invalidate합니다. 이 트래픽이 false sharing의 주범입니다.

#ARM MOESI — Owned 상태 추가

ARM Cortex-A는 MOESI를 사용합니다. Owned 상태가 추가됩니다.

State의미
M (Modified)이 cache만 valid, dirty
O (Owned)여러 cache valid, this가 write-back 책임
E (Exclusive)이 cache만 valid, clean
S (Shared)여러 cache valid, clean
I (Invalid)무효

Owned 상태 덕분에 dirty 데이터를 cache-to-cache로 공유할 수 있습니다. Memory에 write-back을 지연시킬 수 있어 bus traffic이 줄어듭니다.

#Snoop-Based — Bus-Based

Cache A read miss for line X
→ bus broadcast
→ Cache B has X? → respond + change state

모든 cache가 bus를 listen하면서 다른 cache의 트랜잭션에 반응합니다. 구조가 단순하고 latency가 낮습니다.

단점은 bus가 bottleneck이 된다는 점입니다. 8개 이상의 코어가 같은 bus에 연결되면 broadcast traffic이 폭발해 scalability가 떨어집니다.

ARM CCI-400(Cluster Coherence Interconnect)이 snoop-based이며 최대 5 master, 4 cluster까지 연결합니다. 모바일 SoC에서 흔히 사용됩니다.

#Directory-Based

각 line의 home directory가 누가 cache 보유 중인지 기록
Cache A miss → home directory query
Home: cache B,C in S state
→ direct request to B (or C)

각 cache line마다 home node가 있어 어느 코어가 그 line을 가지고 있는지 추적합니다. Bus broadcast 대신 point-to-point 메시지로 처리하므로 scalability가 훨씬 좋습니다.

단점은 directory storage overhead입니다. Line마다 N-bit vector(N = 코어 수)가 필요합니다.

ARM CHI(Coherent Hub Interface)가 directory-based이며 Cortex-A78 이상의 서버급 SoC에서 사용됩니다. Mesh interconnect로 64코어 이상을 지원합니다.

#False Sharing은 Coherency의 부산물

struct {
atomic_int a; /* CPU 0 사용 */
atomic_int b; /* CPU 1 사용 */
} stats; /* 같은 64-byte line */

논리적으로는 두 변수가 독립이지만 같은 cache line에 들어 있어 한쪽이 write할 때마다 다른 쪽 cache가 invalidate됩니다. 매 access마다 coherency traffic이 발생해 throughput이 크게 떨어집니다.

해결은 alignas(64)로 line을 분리하는 것입니다. 자세한 분석은 4-02 편에 있습니다.

#Coherency Overhead 측정

ARMv8 PMU events입니다.

Event의미
r2A (BUS_ACCESS_LD)bus 트래픽 read
r2B (BUS_ACCESS_ST)bus 트래픽 write
r2D (REMOTE_ACCESS)다른 cluster cache access
Snoop hit다른 cache에서 데이터 fetch
Terminal window
perf stat -e r2A,r2B,r2D ./prog

REMOTE_ACCESS가 큰 값을 보이면 cross-cluster traffic이 많다는 의미입니다. Thread를 같은 cluster의 코어로 pin해서 줄일 수 있습니다.

#NUMA — Multi-Socket Coherency

Socket 0: Core 0-7, DRAM 0
Socket 1: Core 8-15, DRAM 1
QPI/UPI (Intel) 또는 CCIX (ARM)로 socket 간 coherent
Remote DRAM access = local의 1.5-2x latency

여러 socket이 있을 때 자기 socket의 메모리에 접근하면 빠르고, 다른 socket의 메모리에 접근하면 느립니다. 그래서 thread와 memory를 같은 NUMA node에 두는 것이 필요합니다.

numactl --cpunodebind=0 --membind=0 ./prog

위 명령은 process를 node 0에 묶어 cross-socket coherency traffic을 0으로 만듭니다.

CXL 2.0과 3.0은 PCIe 기반의 cache coherent interconnect입니다.

CXL.cache — accelerator가 host memory를 coherent하게 access
CXL.mem — host가 device memory를 coherent하게 access
GPU, FPGA, CXL DDR module이 모두 같은 메모리 공간

CXL pool은 수 TB의 unified coherent memory를 만들 수 있어 데이터센터의 차세대 메모리 아키텍처로 주목받고 있습니다.

#ARM ACE와 CHI

Interface용도
AXInon-coherent
ACEfull coherency, cluster 내
ACE-Litepartial, I/O coherency만
CHIscalable mesh, server

ACE는 master-subordinate snoop broadcast 방식이며 CHI는 message-based directory 방식입니다. Embedded SoC는 보통 ACE를 사용하고, server급 SoC가 CHI를 사용합니다.

#Coherency 비활성화

/* Device memory — coherency 자동 없음 */
struct device_mem __iomem *m;
/* Normal memory에서 coherency 끄기 */
MPU->RBAR = ... | MPU_ATTR_NON_SHAREABLE;

DMA buffer처럼 coherency가 불필요한 영역은 non-shareable로 설정해 coherency traffic을 0으로 만들 수 있습니다. 대신 software가 cache maintenance를 명시적으로 해야 합니다.

#False Sharing 측정 — perf c2c

Terminal window
sudo perf c2c record -F 60000 ./prog
sudo perf c2c report
# 출력 예
# Total HITM events: 1234
# Per cache line:
# line 0xABCDEF00: HITM=500, threads=4
# source: src.c:42

HITM(Hit in Modified)은 다른 코어가 Modified 상태로 가지고 있는 line에 접근했을 때 발생하는 이벤트입니다. False sharing의 직접적인 signature이며, perf c2c가 발생 line과 소스 위치까지 알려 줍니다.

#ASIL Lock-Step

ASIL-D ECU:
Lock-step dual core (CPU0 + CPU0_redundant)
두 코어가 cycle 단위로 동일한 명령을 실행
Cache 상태도 동기 유지

자동차의 brake나 steering 같은 ASIL-D 시스템에서는 Cortex-R52의 DCLS(Dual-Core Lock Step) 옵션을 사용합니다. 두 코어가 같은 명령을 cycle 단위로 동기 실행하고, cache 상태도 함께 유지해 결정성을 보장합니다.

#자주 보는 함정과 안티패턴

⚠️ Cortex-M의 cache는 DMA와 coherent하지 않음

DMA write → memory /* cache는 stale */
CPU read /* cache hit, stale data */

Cortex-M cache는 single core용으로 설계되었으며 DMA controller와의 자동 coherency를 지원하지 않습니다. SCB_InvalidateDCache_by_AddrSCB_CleanDCache_by_Addr를 명시적으로 호출해야 합니다.

⚠️ False sharing 무시

perf c2c로 진단하고 alignas(64)로 해결합니다.

⚠️ NUMA-unaware allocation

buf = malloc(huge); /* node 0에 alloc, node 1 thread가 사용 */

numa_alloc_local()이나 thread-local allocator를 사용해야 합니다.

⚠️ Coherency를 일부만 가정

MPU에서 region A는 coherent, region B는 non-coherent로 설정한 뒤 DMA가 region 경계를 넘으면 일부는 coherent하고 일부는 아닌 상태가 되어 동작이 미정의됩니다. DMA buffer는 전체를 non-coherent로 두고 maintenance를 명시하는 것이 안전합니다.

#측정 — 실측 결과

Cortex-A72 4-core cluster에서 측정한 cache coherency 트래픽입니다.

Workload BUS_ACCESS REMOTE_ACCESS Cycles
Local read (cache hit) 0 0 1
Local read (L2 miss) 1 0 15
Snoop hit (다른 cache에서) 1 0 25
Cross-cluster snoop 1 1 80
False sharing (high freq) high low 100+

Cross-cluster snoop이 local L2 miss보다 5배 이상 비쌉니다. Thread affinity로 같은 cluster에 묶는 것이 큰 효과를 줍니다.

#정리

  • MESI는 Modified, Exclusive, Shared, Invalid 4-state 프로토콜입니다.
  • ARM MOESI는 Owned 상태를 추가해 cache-to-cache 공유 효율을 높입니다.
  • Snoop-based(CCI-400)는 단순하지만 scalability가 제한적이며 directory-based(CHI)는 server급에서 사용됩니다.
  • False sharing은 coherency의 부산물이며 perf c2c로 진단합니다.
  • NUMA와 CXL은 multi-socket과 heterogeneous coherency를 다룹니다.
  • Cortex-M cache는 DMA와 non-coherent이므로 software maintenance가 필수입니다.

다음 편은 SMP 성능 분석 — 코어별 부하와 affinity, scalability를 살펴봅니다.

#관련 항목

Embedded Performance Engineering · 39 of 57

  1. 1Embedded Performance Engineering — 임베디드 성능 엔지니어링 시리즈 소개
  2. 2임베디드 성능 분석 방법론 — Measure → Analyze → Optimize 사이클
  3. 3성능 지표 정의 — Latency·Throughput·Utilization 분석
  4. 4성능 측정의 기본 — Wall-Clock·CPU Cycle·Instruction Count
  5. 5성능 데이터 통계적 분석 — Percentile·Histogram·평균의 함정
  6. 6실시간 성능 분석 — WCET·Jitter·Deadline Miss 측정
  7. 7임베디드 벤치마킹 기초 — 재현성·Warmup·노이즈 제거
  8. 8성능 모델링 — Amdahl·Gustafson·Roofline Model 적용
  9. 9프로파일링 기법 개요 — Sampling vs Instrumentation·PGO·LTO
  10. 10CPU 파이프라인 분석 — 5-stage·Cortex-M·Cortex-A 비교
  11. 11Pipeline Stall 분석 — Data·Structural·Control Hazard·Forwarding
  12. 12Branch Prediction 분석 — Static·2-bit·BTB·BHT·Mispredict 비용
  13. 13Speculative Execution 분석 — OoO·Reorder Buffer·Register Renaming
  14. 14CPU Cache 기초 — L1·L2·L3·Set Associative·Replacement Policy
  15. 15Cache Miss 3C Model 분석 — Compulsory·Capacity·Conflict
  16. 16Cache Line 최적화 — Alignment·Prefetch·False Sharing 처리
  17. 17메모리 대역폭 분석 — STREAM·Roofline·Bus Saturation 측정
  18. 18SIMD·NEON 활용 — 128-bit Vector·Auto-Vectorization·SVE/SVE2
  19. 19PMU·HPM 하드웨어 카운터 분석 — 정밀 성능 진단
  20. 20임베디드 Bus Architecture — AHB·AXI·CHI 진화와 5-Channel
  21. 21Bus Contention 진단 — Arbitration·QoS·Starvation 측정
  22. 22DMA 성능 최적화 — Burst·Scatter-Gather·Chain·Cache 일관성
  23. 23DMA vs CPU Copy 성능 비교 — Break-even·Setup Overhead 실측
  24. 24Interrupt Latency 분석 — 진입·종료·Tail-Chaining·Late Arrival
  25. 25Interrupt Storm 처리 — NAPI·Rate-Limit·Polling 전환
  26. 26MMIO 접근 성능 — Cache Policy·Write-Combining·Volatile·Barrier
  27. 27Peripheral Clock 분석 — PLL·Divider·Gating·DVFS
  28. 28Power vs Performance 트레이드오프 — DVFS·Race-to-Idle·Big.LITTLE
  29. 29Thermal Throttling 분석 — Junction Temp·Trip Point·냉각
  30. 30CXL Interconnect 분석 — AI 시대 메모리 대역폭 확장
  31. 31Concurrency 기초 — Concurrency vs Parallelism·Race·Memory Model
  32. 32False Sharing 진단 — Cache Line Ping-Pong·Padding·측정
  33. 33Lock Contention 분석 — Wait·Hold·Convoy·측정 기법
  34. 34Spinlock 성능 분석 — Spin-Wait vs Context Switch·Ticket·MCS
  35. 35Mutex 성능 분석 — Futex·Adaptive·Priority Inheritance
  36. 36Reader-Writer Lock 성능 — Reader/Writer Priority·RCU·Seqlock
  37. 37Lock-Free 자료구조 성능 — CAS·ABA·Hazard Pointer·Epoch Reclamation
  38. 38Memory Ordering 분석 — Acquire·Release·Seq-Cst·ARM Relaxed Model
  39. 39Cache Coherency 프로토콜 — MESI·MOESI·Snoop·Directory
  40. 40SMP 성능 분석 — Per-Core·Affinity·Load Balance·Scalability
  41. 41Linux perf 기초 — stat·record·report 활용
  42. 42Linux perf 고급 — Raw Event·Tracepoint·perf script
  43. 43ftrace 활용 — function·function_graph·latency tracer
  44. 44eBPF·bpftrace 동적 트레이싱 — 커널 무수정 관측
  45. 45Flamegraph 분석 — On-CPU·Off-CPU·Differential
  46. 46ARM DS·Lauterbach 분석 — Hardware Trace 전문 도구
  47. 47Bare-metal 프로파일링 — GPIO·DWT·SysTick·ITM 활용
  48. 48NVIDIA Nsight Systems — GPU·NPU 포함 시스템 분석
  49. 49모던 프로파일러 비교 — Tracy·Hotspot·uftrace·Coz
  50. 50연속 프로파일링 — Parca·Pixie·Pyroscope·Tetragon
  51. 51실전 사례 — ISR Latency 100µs Deadline Miss 추적
  52. 52실전 사례 — Matrix Multiply가 예상의 10배 느린 이유
  53. 53실전 사례 — 8-core가 4-core를 넘으면 throughput이 떨어지는 이유
  54. 54실전 사례 — 카메라 1080p 60fps가 30fps로 떨어지는 이유
  55. 55CXL.mem 지연·대역폭 실측 — Direct·Switch·Pooled 토폴로지 비교
  56. 56CXL 성능 프로파일링 도구 — cxl-cli·DAMON·perf-mem 활용
  57. 57실전 사례 — CXL.mem 추가로 LLM inference KV cache 처리량 회복