임베디드 성능 분석 방법론 — Measure → Analyze → Optimize 사이클
#한 줄 요약
“측정 → 가설 → 검증” 사이클이 핵심입니다. 추측으로 코드를 바꾸지 않습니다. 데이터가 답합니다.
#왜 방법론이 필요한가
“느려요”라는 보고가 들어오면 80% 개발자가 코드를 읽으면서 추측합니다. “여기일 것 같다”고 결론짓고 수정한 뒤 측정해 보면 변화가 없습니다. 그래서 다른 곳을 또 만지고, 이렇게 추측과 수정을 반복합니다.
이렇게 작업하면 시간만 낭비됩니다. 더 큰 문제는 원래 안 느렸던 코드까지 망가뜨릴 수 있다는 점입니다.
방법론은 추측 대신 측정하고, 체계적으로 좁혀 가는 일을 의미합니다.
#Brendan Gregg의 USE 메서드
각 자원(CPU, memory, disk, network)에 대해 세 가지 지표를 확인합니다.
| 지표 | 의미 |
|---|---|
| Utilization | 자원 사용률 (%) |
| Saturation | 대기열 길이 (queueing) |
| Errors | 오류 카운트 |
| 시나리오 | 진단 |
|---|---|
| CPU U=95%, S=0, E=0 | CPU bound |
| CPU U=50%, S=10, E=0 | run queue 적체 (스케줄링 문제) |
| CPU U=50%, S=0, E=많음 | 다른 곳 (디스크·메모리) |
먼저 모든 자원의 3 지표를 훑어 병목을 찾고, 그 다음에 deep dive로 들어갑니다.
#RED 메서드 (Service-Oriented)
서비스 endpoint에 대해 다음 세 지표를 봅니다.
| 의미 | |
|---|---|
| Rate | 초당 요청 수 |
| Errors | 실패율 |
| Duration | latency 분포 (p50, p99, p999) |
임베디드에서는 task별로 적용합니다. 즉, 매 task의 처리 빈도, 오류, 처리 시간을 추적합니다.
#임베디드 적용
| 자원 | Utilization | Saturation | Errors |
|---|---|---|---|
| CPU | run 시간 / total | run queue 길이 | watchdog reset |
| RAM | heap used / total | OOM 카운트 | malloc 실패 |
| Flash | bandwidth 사용률 | wait state | ECC 오류 |
| Bus (AXI·AHB) | 트랜잭션/sec | outstanding TX | bus fault |
| Cache | hit rate | line refill stall | (없음) |
| DMA | 채널 사용률 | 대기 descriptor | DMA error |
| ISR | duty cycle | nested depth | spurious IRQ |
| Mutex | hold time | wait list 길이 | timeout |
각 행을 모니터링하다가 비정상을 발견하면 drill down으로 좁혀 갑니다.
#측정 → 가설 → 검증 사이클
#Step 1: Measure
uint32_t t1 = DWT->CYCCNT;suspicious_function();uint32_t t2 = DWT->CYCCNT;log_max(t2 - t1);여러 input을 시도하면서 분포를 수집합니다. 평균보다 p99·max가 중요합니다.
#Step 2: Hypothesize
“이 함수가 cache miss로 느리다”처럼 구체적인 가설을 세웁니다.
#Step 3: Verify
PMU counter를 활성화합니다.
// L1D cache miss countread_pmu(L1D_CACHE_REFILL);수치가 예측과 일치하면 가설이 맞은 것이므로 해결책을 적용합니다. 일치하지 않으면 다른 가설을 세웁니다.
#흔한 함정
#1. 평균만 본다
| 지표 | 값 | 판정 |
|---|---|---|
| Avg latency | 10 ms | 좋아 보임 |
| p99 | 50 ms | 나쁨 |
| max | 500 ms | 끔찍 |
Real-time에서는 worst case가 진실입니다. 그래서 항상 percentile과 max를 함께 봅니다.
#2. 측정 환경 = 실 환경 가정
벤치 환경은 cool cache에 no contention이라 빠르게 나옵니다. 반대로 실 환경은 hot system, bus saturation 상태라 훨씬 느립니다.
실 환경에서 오랜 시간에 걸쳐 측정해야 합니다.
#3. Observer Effect
측정 코드 자체가 시스템에 영향을 줍니다. printf나 trace overhead가 대표적입니다. 그래서 DWT 카운터, ring buffer 같은 light-weight tracing을 씁니다.
#4. Single Run
한 번 측정하고 “이 정도구나”라고 결론을 내리면 다음 실행에서는 완전히 다른 결과가 나옵니다. 최소 N=100회 측정으로 분포를 확인해야 합니다.
#임베디드 특화 — Bottleneck 우선순위
대부분의 임베디드 시스템에서 문제 발생 빈도는 다음과 같습니다.
- ISR 길거나 nested (40%)
- Critical section / lock contention (25%)
- Memory bandwidth (15%)
- Cache miss (10%)
- DMA bottleneck (5%)
- 기타 (5%)
처음에는 ISR과 lock부터 확인합니다. Cache와 DMA는 그 다음 순서입니다.
#Roofline Model 미리보기
성능은 *min(peak compute, memory bandwidth)*로 결정됩니다. 자세한 내용은 1-07에서 다룹니다.
Memory-bound 코드는 CPU를 아무리 빠르게 해도 변화가 없습니다. 진단이 먼저입니다.
#도구 분류 — 1-08에서 자세히
| 카테고리 | 도구 |
|---|---|
| Sampling | perf, gprof |
| Tracing | ftrace, LTTng, SystemView |
| HW counter | PMU, DWT, ITM |
| Profiling visualization | Flamegraph, Tracealyzer |
| 베어메탈 | GPIO + 로직 분석기 |
#자주 하는 실수
⚠️ “여기일 것 같다”
코드 읽기만으로 결정하면 80%는 틀립니다. 항상 측정합니다.
⚠️ Premature optimization
Donald Knuth가 “premature optimization is the root of all evil”이라고 했습니다. 측정한 다음에 최적화합니다.
⚠️ Micro-optimization
10% 시간만 차지하는 코드를 100배 빠르게 해도 전체로는 9% 개선에 그칩니다. 90% 코드를 2배 개선하는 쪽이 훨씬 큽니다.
⚠️ Optimize after release
production 데이터가 최고의 input입니다. 출시 후 실 사용 환경에서 프로파일링을 수행합니다.
#정리
- 방법론 = 측정 → 가설 → 검증입니다. 추측은 금지입니다.
- USE(Utilization·Saturation·Errors)와 RED(Rate·Errors·Duration)를 함께 씁니다.
- 임베디드에서는 모든 자원에 USE를 적용합니다(CPU·RAM·Bus·Cache·DMA·ISR·Mutex).
- 평균이나 single run의 함정을 피하려면 분포와 다중 측정이 필요합니다.
- ISR과 Lock이 *임베디드 bottleneck 상위 65%*를 차지합니다.
다음 편은 성능 지표 정의입니다. Latency·Throughput·Utilization을 다룹니다.
#관련 항목
Embedded Performance Engineering · 2 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 처리량 회복
관련 글
성능 지표 정의 — Latency·Throughput·Utilization 분석
3 핵심 지표 + 임베디드 추가 — Jitter·Deadline. Service time vs Response time.
Embedded Performance Engineering — 임베디드 성능 엔지니어링 시리즈 소개
왜 느린가? Cache miss, pipeline stall, bus contention부터 profiling 도구 활용까지. 임베디드 시스템 성능 분석의 모든 것.
실전 사례 — CXL.mem 추가로 LLM inference KV cache 처리량 회복
70B 모델 KV cache가 HBM 한계를 넘어 throughput이 무너졌을 때, CXL.mem 256 GB pool 추가로 회복한 실전 케이스.
이 글을 참조하는 글 (5)
- End-to-End Driver + RTL Co-simulation — 실전 통합 흐름— Driver-RTL Co-simulation
- 실전 사례 — 카메라 1080p 60fps가 30fps로 떨어지는 이유— Embedded Performance Engineering
- Linux perf 기초 — stat·record·report 활용— Embedded Performance Engineering
- 성능 지표 정의 — Latency·Throughput·Utilization 분석— Embedded Performance Engineering
- Embedded Performance Engineering — 임베디드 성능 엔지니어링 시리즈 소개— Embedded Performance Engineering