Pipeline Stall 분석 — Data·Structural·Control Hazard·Forwarding
#한 줄 요약
Stall은 pipeline bubble입니다. 명령이 진행하지 못하면서 IPC가 손실됩니다.
#Data Hazard 3종 (RAW·WAR·WAW)
#RAW (Read After Write) — 진짜 의존성
add r0, r1, r2 ; r0 = r1 + r2sub r3, r0, r4 ; r0 사용 — RAWsub가 r0를 읽을 때 add 결과가 필요합니다. Forwarding으로 해결합니다.
#WAR (Write After Read) — 반의존성
add r0, r1, r2 ; r1 readsub r1, r3, r4 ; r1 writeIn-order pipeline에서는 문제가 없습니다. OoO에서는 register renaming으로 해결합니다.
#WAW (Write After Write) — 출력 의존성
add r0, r1, r2sub r0, r3, r4 ; r0 다시 writeIn-order는 자동으로 처리되고, OoO는 renaming으로 처리합니다.
#Forwarding (Bypass)
EX 단계의 출력을 다음 명령의 EX 입력에 직접 연결합니다. 별도 wire로 register file을 우회합니다. ARM Cortex-A에는 Operand Forwarding Unit이 있습니다.
#Load-Use Stall — Forwarding 불가능 케이스
ldr r0, [r1] F D E M W ↑ load 결과 = M 단계 끝add r2, r0, r3 F D E ; E 단계에 r0 필요 — but M 단계 안 끝남 ─── 1 cycle bubble ───ARM Cortex-M3/M4의 load-use penalty는 1 cycle입니다. Cortex-M7은 2 cycle입니다.
#해결책은 명령 재정렬
; 회피ldr r0, [r1]add r2, r0, r3 ; ← stall
; Goodldr r0, [r1]add r4, r5, r6 ; 독립 명령 삽입add r2, r0, r3 ; ← load 결과 사용 시점에 준비됨-O2 이상에서 컴파일러가 자동으로 재정렬합니다. volatile 변수는 순서가 고정되어 재정렬되지 않습니다.
#Structural Hazard
; 가상 — 단일 memory port 가정ldr r0, [r1] ; F D E M ← memoryldr r2, [r3] ; F D E ← M 단계에 또 memory 시도 → stallHarvard architecture를 쓰면 instruction memory와 data memory가 분리되어 동시에 액세스할 수 있습니다.
ARM Cortex-M3/M4는 single port Harvard입니다 (I/D 통합 bus). M7은 dual port TCM을 가집니다.
#Control Hazard
beq r0, r1, label ; F D E ↑ E 단계에 분기 확정nop ; F (이미 fetch — 분기 시 flush)nop ; Fbranch prediction으로 해결합니다. 별도 편에서 다룹니다.
#ARM Cortex-M4 Cycle 측정 예
volatile uint32_t a, b, c, d;
void test_no_stall(void) { /* 독립 명령 — stall 없음 */ asm volatile ( "add r0, r1, r2 \n" "add r3, r4, r5 \n" "add r6, r7, r8 \n" );}
void test_raw_chain(void) { /* RAW chain — forwarding으로 처리 */ asm volatile ( "add r0, r1, r2 \n" "add r3, r0, r4 \n" /* r0 의존 */ "add r5, r3, r6 \n" /* r3 의존 */ );}
void test_load_use(void) { asm volatile ( "ldr r0, [%0] \n" "add r1, r0, r0 \n" /* load-use stall 1 cycle */ :: "r"(&a) );}DWT CYCCNT로 측정하면 test_no_stall과 test_raw_chain은 같은 cycle이고, test_load_use는 1 cycle이 더 걸립니다.
#PMU STALL Counter (Cortex-A)
Cortex-A53 Performance Monitoring Unit의 이벤트는 다음과 같습니다.
| Event | 의미 |
|---|---|
0x23 STALL_FRONTEND | F·D 단계 stall입니다 (cache miss·branch mispredict) |
0x24 STALL_BACKEND | E·M 단계 stall입니다 (data dependency·memory) |
0x73 STALL_BACKEND_MEM | memory bound stall입니다 |
/* perf_event_open으로 측정 */struct perf_event_attr attr = { .type = PERF_TYPE_RAW, .config = 0x24, // STALL_BACKEND};int fd = perf_event_open(&attr, 0, -1, -1, 0);STALL_FRONTEND > STALL_BACKEND이면 fetch bound입니다 (cache miss나 mispredict가 의심됩니다).
STALL_BACKEND > STALL_FRONTEND이면 compute나 memory bound입니다 (data dependency나 DRAM 대기입니다).
#Out-of-Order Renaming
Cortex-A72 등 OoO 코어의 동작은 다음과 같습니다.
ISA 레벨: r0 = r1 + r2 r3 = r0 + r4 r0 = r5 + r6 (WAW!) r7 = r0 + r8
Renaming 후: v10 = v1 + v2 v11 = v10 + v4 v12 = v5 + v6 (WAW 해소) v13 = v12 + v8Architectural register r0의 두 정의가 physical register 두 개로 분리됩니다. 의존성 cycle이 없는 분리된 stream으로 실행할 수 있습니다.
#Conditional Execution — Cortex-M4 (Thumb-2 IT)
cmp r0, r1it ltmovlt r2, #1 ; if (r0 < r1) r2 = 1; else nothing분기 없이 conditional move를 수행하여 control hazard를 회피합니다. 짧은 if-then 패턴에 최적입니다.
; 회피 (branch hazard)cmp r0, r1bge skipmov r2, #1skip:
; Good (no branch)cmp r0, r1it ltmovlt r2, #1다만 Cortex-A는 IT block 효율이 떨어집니다. 그래서 컴파일러가 자동으로 판단합니다.
#NEON·DSP — SIMD로 Latency Hiding
; 4 element 합산 — scalaradd r0, r1, r2add r0, r0, r3 ; RAW chain (4 cycle)add r0, r0, r4add r0, r0, r5
; NEON SIMDvadd.f32 q0, q1, q2 ; 4 elements 동시 — 1 cycleSIMD는 수평적 병렬화이므로 RAW chain을 우회합니다.
#자주 하는 실수
⚠️ -O0에서 stall 측정
gcc -O0 -o test test.c# 의미 없음 — 컴파일러가 명령 재정렬 안 함, 결과 inconsistent성능 측정은 최소 -O2에서 해야 합니다.
⚠️
volatile로 모든 변수 표시
volatile uint32_t counter; // ← 모든 access fencecounter++; // load + add + store, 재정렬 금지성능 critical loop에서 volatile는 컴파일러 최적화를 차단합니다. register와 통신 register에만 volatile을 씁니다.
⚠️ Branch가 항상 stall이라 가정
Modern CPU에서는 predict가 성공하면 stall = 0입니다. Branch 자체가 문제가 아니라 misprediction이 문제입니다.
⚠️ Forwarding 의존성 무시
for (int i = 0; i < N; i++) { x = x + a[i]; ; ← RAW chain — 한 cycle 1 add만}Loop unroll로 독립 accumulator를 만듭니다.
for (int i = 0; i < N; i += 4) { x0 += a[i]; x1 += a[i+1]; x2 += a[i+2]; x3 += a[i+3];}sum = x0 + x1 + x2 + x3;#정리
- Stall은 pipeline bubble이며, IPC 손실로 이어집니다.
- RAW (data dependency)는 forwarding으로 해결합니다.
- Load-use는 forwarding이 불가하여 1-2 cycle penalty가 있습니다.
- PMU STALL_FRONTEND와 STALL_BACKEND로 원인을 추정합니다.
- 컴파일러
-O2, loop unroll, SIMD로 stall을 회피합니다.
다음 편은 Branch Prediction입니다.
#관련 항목
Embedded Performance Engineering · 11 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 처리량 회복
관련 글
CPU 파이프라인 분석 — 5-stage·Cortex-M·Cortex-A 비교
Fetch·Decode·Execute·Memory·Writeback의 5-stage 파이프라인을 봅니다. Cortex-M3/M4는 3-stage, Cortex-A는 8~15-stage입니다.
DMA vs CPU Copy 성능 비교 — Break-even·Setup Overhead 실측
DMA setup overhead. CPU memcpy 최적화. Break-even size. 실측 데이터.
Speculative Execution 분석 — OoO·Reorder Buffer·Register Renaming
Out-of-order execution. ROB·issue queue·rename. Spectre 측면. Cortex-A 사례.