CPU Cache 기초 — L1·L2·L3·Set Associative·Replacement Policy
#한 줄 요약
**“Cache = locality 활용”**입니다. 자주 쓰는 데이터를 코어 가까이 둡니다.
#Memory Hierarchy
| 레벨 | 접근 시간 | 크기 (Cortex-A72) | 위치 |
|---|---|---|---|
| Register | 0 cycle | 32 × 32-bit | CPU 내부 |
| L1 Cache | 3-4 cycle | I + D | CPU core 옆 |
| L2 Cache | 10-15 cycle | 1MB shared (4-core) | Cluster |
| L3 Cache (있다면) | 30-50 cycle | 4-8MB | SoC 공유 |
| DRAM | 100-300 cycle | GB | external |
매 단계마다 약 10배 느려지고 10배 커집니다. 그림으로 보면 hierarchy의 폭과 latency가 한눈에 들어옵니다.
#L1 — Split I/D (Harvard)
- L1 I-cache — 명령 전용 (read-only, no write-back)
- L1 D-cache — 데이터 (read + write)
fetch와 load/store가 동시에 가능해 structural hazard를 회피합니다.
#L2/L3 — Unified
L2 이상은 instruction과 data를 통합합니다. Inclusive와 Exclusive로 나뉩니다.
| 정책 | 의미 | 사용처 |
|---|---|---|
| Inclusive | L1의 데이터는 L2에도 있음 | Intel (snoop 효율) |
| Exclusive | L1의 데이터는 L2에 없음 | ARM Cortex-A (용량 효율) |
| NINE (Non-Inclusive Non-Exclusive) | 가능하나 보장 안 함 | 최신 Intel L3 |
#Cache Line
일반적 line size — 64 bytes (Cortex-A, x86) 32 bytes (Cortex-M7) 16 bytes (Cortex-M4 cache)CPU가 1 byte만 읽어도 전체 line이 fetch되어 spatial locality를 활용합니다.
int arr[1024];arr[0]; // 64 byte line fetch: arr[0]~arr[15] 캐시 입력arr[1]; // ← 같은 line, hit!arr[16]; // ← 다른 line, miss#Direct Mapped Cache
Address bits:
- [Tag] [Index] [Offset]
- 20 8 6 (64 KB cache, 64 byte line)
각 메모리 주소는 유일한 cache line에만 들어갑니다.
addr 0x1000 → line 64addr 0x5000 → line 64 (conflict! evict 0x1000)addr 0x9000 → line 64 (conflict!)Conflict miss가 빈번합니다. hardware는 단순하지만 hit rate가 낮습니다.
#N-Way Set Associative
[Tag][Index][Offset]Index → set (한 set에 N개 way)
Set 64: way 0: [tag=0x1, data=...] way 1: [tag=0x5, data=...] way 2: [tag=0x9, data=...] way 3: [tag=...]Cortex-A72 L1 D = 4-way set associative. Cortex-A72 L2 = 16-way.
addr 0x1000 hits set X way 0addr 0x5000 hits set X way 1 ← coexist!Hit rate가 올라가고 hardware complexity도 함께 올라갑니다.
#Fully Associative
모든 cache line이 임의 데이터를 보유할 수 있습니다. Translation Lookaside Buffer (TLB)의 작은 부분이 fully associative 구조로 되어 있습니다.
Lookup: compare tag with ALL entries — 비싸지만 conflict 0용량이 작을 때만 씁니다 (8-32 entry).
#Replacement Policy
#LRU (Least Recently Used)
각 way의 access 시간을 timestamp로 저장합니다. Eviction 시 가장 오래된 것을 폐기합니다.
Way: 0 1 2 3Time: 5 8 3 10 → 다음 miss 시 way 2 (가장 오래된) 폐기4-way 정도까지는 정확하게 구현하지만 그 이상은 비용이 큽니다.
#Pseudo-LRU (PLRU) — 표준
Binary tree 형태로 log N bit으로 근사합니다.
4-way PLRU (3 bits): [root: 0/1] / \ [0/1] [0/1] / \ / \ way 0 way 1 way 2 way 3각 노드의 bit가 마지막 access의 반대 방향을 가리키며, eviction은 그 방향을 따라 내려갑니다.
근사적이지만 간단하고 빠릅니다. ARM Cortex-A53이 PLRU를 사용합니다.
#Random
진짜 random이거나 round-robin 방식입니다. WCET 예측이 가능해 자동차 인증에서 자주 사용합니다 (예측이 어려운 LRU를 회피).
#Write Policy
#Write-Through
Write → cache + memory 둘 다 즉시 업데이트장점은 coherence가 단순하다는 점입니다. 단점은 write traffic이 크다는 점입니다.
#Write-Back
Write → cache만 (dirty bit set)Eviction or flush → memory 업데이트장점은 write traffic이 적다는 점입니다. 단점은 DMA·SMP coherence가 복잡하다는 점입니다.
ARM L1 D-cache는 write-back에 write-allocate 방식입니다.
#Write-Allocate vs No-Write-Allocate
*ptr = 42; // write miss- Write-allocate: miss 시 line을 fetch하고 cache에 write
- No-write-allocate: miss 시 cache를 거치지 않고 memory에 직행
Streaming write(한 번 쓰고 다시 읽지 않는 패턴)에는 no-write-allocate가 효율적입니다.
#Cortex-M Cache
| MCU | Cache |
|---|---|
| Cortex-M0/M3/M4 | 없음 (TCM 또는 직접 flash 실행) |
| Cortex-M7 | L1 I + L1 D (선택적 enable) |
| Cortex-M33 | optional |
| Cortex-M55 | optional |
Cortex-M7 cache enable:
SCB_EnableICache();SCB_EnableDCache();DMA 사용 시 cache maintenance가 필수입니다. SCB_CleanDCache_by_Addr나 SCB_InvalidateDCache_by_Addr를 사용합니다.
#TCM (Tightly Coupled Memory)
__attribute__((section(".dtcm"))) uint8_t fast_buf[4096];__attribute__((section(".itcm"))) void critical_isr(void) { ... }Cache miss가 없는 결정성 메모리입니다. Cortex-M7·R52 등에 32-256 KB가 내장되어 있고 자동차·항공기 critical loop에 사용됩니다.
#측정 — Cache Miss Rate
perf stat -e cache-references,cache-misses ./prog
# miss-rate = misses / references# < 5% — 좋음# 10-20% — 보통# > 30% — 문제PMU event:
L1D_CACHEL1D_CACHE_REFILL(Cortex-A)L1I_CACHEL1I_CACHE_REFILLL2D_CACHEL2D_CACHE_REFILL
#자주 하는 실수
⚠️ 큰 array 순회 시 cache line 무시
struct Big { int id; char name[60]; int value; };for (i = 0; i < N; i++) sum += arr[i].value;// 64 byte struct → line당 1개 → memory bandwidth 낭비→ SoA(Structure of Arrays)를 쓰거나 value만 별도 array로 빼내야 합니다.
⚠️ Stride access
for (j = 0; j < W; j++) for (i = 0; i < H; i++) sum += matrix[i][j]; // ← column-major access // 매 access cache miss→ row-major 방식으로 안쪽 loop을 j로 두어야 합니다 (matrix[i][j]).
⚠️ DMA 후 cache invalidate 안 함
HAL_UART_Receive_DMA(&huart, buf, len);// DMA 끝났는데printf("%s", buf); // ← cache가 옛 데이터 보여줌SCB_InvalidateDCache_by_Addr(buf, len); 호출이 필요합니다.
⚠️ Cache 활성화 후 Variable alignment
DMA buffer는 cache line aligned여야 하며 32-byte align을 권장합니다.
__attribute__((aligned(32))) uint8_t dma_buf[256];#정리
- Hierarchy는 L1 split(I+D)에서 L2/L3 unified, DRAM 순으로 이어집니다.
- Cache line은 Cortex-A에서 64 byte, Cortex-M7에서 32 byte입니다.
- N-way set associative는 Cortex-A에서 4-16 way로 사용합니다.
- Replacement는 LRU, PLRU, Random(WCET용)으로 나뉩니다.
- Write-back + write-allocate가 표준입니다.
- Cortex-M7에서 cache와 DMA를 함께 쓸 때는 cache maintenance가 필수입니다.
다음 편은 Cache Miss 분석으로 3C model을 다룹니다.
#관련 항목
Embedded Performance Engineering · 14 of 57
- 1Embedded Performance Engineering — 임베디드 성능 엔지니어링 시리즈 소개
- 2임베디드 성능 분석 방법론 — Measure → Analyze → Optimize 사이클
- 3성능 지표 정의 — Latency·Throughput·Utilization 분석
- 4성능 측정의 기본 — Wall-Clock·CPU Cycle·Instruction Count
- 5성능 데이터 통계적 분석 — Percentile·Histogram·평균의 함정
- 6실시간 성능 분석 — WCET·Jitter·Deadline Miss 측정
- 7임베디드 벤치마킹 기초 — 재현성·Warmup·노이즈 제거
- 8성능 모델링 — Amdahl·Gustafson·Roofline Model 적용
- 9프로파일링 기법 개요 — Sampling vs Instrumentation·PGO·LTO
- 10CPU 파이프라인 분석 — 5-stage·Cortex-M·Cortex-A 비교
- 11Pipeline Stall 분석 — Data·Structural·Control Hazard·Forwarding
- 12Branch Prediction 분석 — Static·2-bit·BTB·BHT·Mispredict 비용
- 13Speculative Execution 분석 — OoO·Reorder Buffer·Register Renaming
- 14CPU Cache 기초 — L1·L2·L3·Set Associative·Replacement Policy
- 15Cache Miss 3C Model 분석 — Compulsory·Capacity·Conflict
- 16Cache Line 최적화 — Alignment·Prefetch·False Sharing 처리
- 17메모리 대역폭 분석 — STREAM·Roofline·Bus Saturation 측정
- 18SIMD·NEON 활용 — 128-bit Vector·Auto-Vectorization·SVE/SVE2
- 19PMU·HPM 하드웨어 카운터 분석 — 정밀 성능 진단
- 20임베디드 Bus Architecture — AHB·AXI·CHI 진화와 5-Channel
- 21Bus Contention 진단 — Arbitration·QoS·Starvation 측정
- 22DMA 성능 최적화 — Burst·Scatter-Gather·Chain·Cache 일관성
- 23DMA vs CPU Copy 성능 비교 — Break-even·Setup Overhead 실측
- 24Interrupt Latency 분석 — 진입·종료·Tail-Chaining·Late Arrival
- 25Interrupt Storm 처리 — NAPI·Rate-Limit·Polling 전환
- 26MMIO 접근 성능 — Cache Policy·Write-Combining·Volatile·Barrier
- 27Peripheral Clock 분석 — PLL·Divider·Gating·DVFS
- 28Power vs Performance 트레이드오프 — DVFS·Race-to-Idle·Big.LITTLE
- 29Thermal Throttling 분석 — Junction Temp·Trip Point·냉각
- 30CXL Interconnect 분석 — AI 시대 메모리 대역폭 확장
- 31Concurrency 기초 — Concurrency vs Parallelism·Race·Memory Model
- 32False Sharing 진단 — Cache Line Ping-Pong·Padding·측정
- 33Lock Contention 분석 — Wait·Hold·Convoy·측정 기법
- 34Spinlock 성능 분석 — Spin-Wait vs Context Switch·Ticket·MCS
- 35Mutex 성능 분석 — Futex·Adaptive·Priority Inheritance
- 36Reader-Writer Lock 성능 — Reader/Writer Priority·RCU·Seqlock
- 37Lock-Free 자료구조 성능 — CAS·ABA·Hazard Pointer·Epoch Reclamation
- 38Memory Ordering 분석 — Acquire·Release·Seq-Cst·ARM Relaxed Model
- 39Cache Coherency 프로토콜 — MESI·MOESI·Snoop·Directory
- 40SMP 성능 분석 — Per-Core·Affinity·Load Balance·Scalability
- 41Linux perf 기초 — stat·record·report 활용
- 42Linux perf 고급 — Raw Event·Tracepoint·perf script
- 43ftrace 활용 — function·function_graph·latency tracer
- 44eBPF·bpftrace 동적 트레이싱 — 커널 무수정 관측
- 45Flamegraph 분석 — On-CPU·Off-CPU·Differential
- 46ARM DS·Lauterbach 분석 — Hardware Trace 전문 도구
- 47Bare-metal 프로파일링 — GPIO·DWT·SysTick·ITM 활용
- 48NVIDIA Nsight Systems — GPU·NPU 포함 시스템 분석
- 49모던 프로파일러 비교 — Tracy·Hotspot·uftrace·Coz
- 50연속 프로파일링 — Parca·Pixie·Pyroscope·Tetragon
- 51실전 사례 — ISR Latency 100µs Deadline Miss 추적
- 52실전 사례 — Matrix Multiply가 예상의 10배 느린 이유
- 53실전 사례 — 8-core가 4-core를 넘으면 throughput이 떨어지는 이유
- 54실전 사례 — 카메라 1080p 60fps가 30fps로 떨어지는 이유
- 55CXL.mem 지연·대역폭 실측 — Direct·Switch·Pooled 토폴로지 비교
- 56CXL 성능 프로파일링 도구 — cxl-cli·DAMON·perf-mem 활용
- 57실전 사례 — CXL.mem 추가로 LLM inference KV cache 처리량 회복
관련 글
실전 사례 — Matrix Multiply가 예상의 10배 느린 이유
1024×1024 matrix multiply가 이론값의 10배 느렸다. SIMD부터 의심했지만 진짜 범인은 캐시 미스 90%였다.
DMA 성능 최적화 — Burst·Scatter-Gather·Chain·Cache 일관성
Burst size 최적화. Scatter-gather, chain. Cache clean/invalidate, double buffer.
Cache Line 최적화 — Alignment·Prefetch·False Sharing 처리
64-byte line alignment, software prefetch, false sharing 회피, SoA·AoS 선택.