성능 측정의 기본 — Wall-Clock·CPU Cycle·Instruction Count
#한 줄 요약
**“무엇을 측정하느냐가 절반”**입니다. wall-clock, cycle, instruction은 각각 다른 답을 줍니다.
#3 종류의 시간
#Wall-Clock Time
실제 경과 시간입니다. 사용자가 체감하는 값과 같습니다.
struct timespec t1, t2;clock_gettime(CLOCK_MONOTONIC_RAW, &t1);work();clock_gettime(CLOCK_MONOTONIC_RAW, &t2);long ns = (t2.tv_sec - t1.tv_sec) * 1e9 + (t2.tv_nsec - t1.tv_nsec);직관적이라는 장점이 있습니다. 다만 context switch, IRQ, DMA가 포함되면 코드 자체의 시간으로는 부정확해집니다.
#CPU Cycle (CPU Time)
실제 코드가 실행된 cycle입니다. IRQ나 idle은 제외됩니다.
// Cortex-MDWT->CYCCNT = 0;work();uint32_t cycles = DWT->CYCCNT;pure compute time에 가깝습니다. CYCCNT는 cycle을 그대로 세기 때문에 context switch의 영향을 받지 않습니다.
#Instruction Count
PMU의 INST_RETIRED event를 씁니다.
read_pmu(INST_RETIRED);work();uint64_t insts = read_pmu(INST_RETIRED) - start;IPC는 inst / cycle로 계산합니다. architecture 효율을 분석할 때 씁니다.
#Bare-Metal — DWT_CYCCNT
Cortex-M3+에 내장된 32-bit cycle counter입니다.
// Initialization (once)CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT = 0;DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
// Useuint32_t start = DWT->CYCCNT;work();uint32_t cycles = DWT->CYCCNT - start;uint32_t us = cycles / (SystemCoreClock / 1000000);1-cycle 정밀이 장점입니다. 32-bit @ 168 MHz에서는 약 25초마다 wraparound가 일어나므로 짧은 측정에만 적합합니다.
#Cortex-M0 — DWT 없음
ARMv6-M에는 DWT가 없습니다. 그래서 SysTick을 씁니다.
uint32_t systick_get_us(void) { return (SysTick->LOAD - SysTick->VAL) / (SystemCoreClock / 1000000);}Reload value에 의존합니다. 카운터가 reload될 때 tick interrupt가 발생하므로, 측정이 1 tick을 넘으면 누적해서 계산해야 합니다.
#Linux — clock_gettime
struct timespec t;clock_gettime(CLOCK_MONOTONIC_RAW, &t);// CLOCK_MONOTONIC_RAW = NTP·adjustment 영향 X// CLOCK_PROCESS_CPUTIME_ID = 이 프로세스 CPU 시간만// CLOCK_THREAD_CPUTIME_ID = 이 thread만각 clock의 의미는 다음과 같습니다.
| Clock | 의미 |
|---|---|
CLOCK_REALTIME | Wall clock (date) — 변경 가능 |
CLOCK_MONOTONIC | 부팅 후 시간 — NTP 조정 |
CLOCK_MONOTONIC_RAW | NTP 조정 없음 — 측정에 적합 |
CLOCK_PROCESS_CPUTIME_ID | 이 process CPU 시간 |
CLOCK_THREAD_CPUTIME_ID | 이 thread CPU 시간 |
#RDTSC — x86
Intel과 AMD의 cycle counter입니다.
static inline uint64_t rdtsc(void) { uint32_t lo, hi; __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); return ((uint64_t)hi << 32) | lo;}⚠️ out-of-order execution 때문에 진짜 측정에는 rdtscp와 cpuid barrier가 필요합니다.
#PMU — Hardware Performance Counters
Cortex-A와 Cortex-M55+에 있는 Performance Monitor Unit입니다. 다양한 event를 카운트할 수 있습니다.
// ARMv8 cortex-a (EL1)uint64_t cnt;asm volatile ("mrs %0, pmccntr_el0" : "=r"(cnt)); // CPU cycles흔히 쓰는 event는 다음과 같습니다.
CPU_CYCLES(0x11)INST_RETIRED(0x08)L1D_CACHE_REFILL(0x03)L2D_CACHE_REFILL(0x17)BR_MIS_PRED(0x10)
각 PMU는 제한된 counter 수(보통 4-6개)만 가집니다. Linux의 perf는 다중 event를 측정할 때 time-multiplex로 처리합니다.
#GPIO + 로직 분석기 — 외부 측정
가장 비침습적인 방법입니다. 코드에 GPIO toggle만 추가합니다.
void critical_function(void) { GPIO_SET(DEBUG_PIN); work(); GPIO_CLR(DEBUG_PIN);}Saleae나 Sigrok으로 펄스 폭을 측정합니다. CPU 자체의 측정 도구가 가지는 overhead가 0입니다.
#Latency between events
ISR_ENTRY: GPIO_TOGGLE(IRQ_PIN);// ...ISR_EXIT;
TASK_START: GPIO_TOGGLE(TASK_PIN);두 pin 사이의 시간 차로 scheduler latency를 측정합니다.
#Overhead 줄이기
측정 코드 자체가 시스템에 영향을 줍니다.
| 측정 | Overhead |
|---|---|
| DWT_CYCCNT read | 1 cycle |
| GPIO toggle | 2-4 cycle |
clock_gettime (Linux user) | ~30 ns |
printf | 수 µs-ms |
| Trace ring buffer | 수십 cycle |
Lightweight tracing의 예는 다음과 같습니다.
struct trace_record { uint32_t cycles; uint16_t event_id; uint16_t data;};static struct trace_record trace_buf[TRACE_SIZE];static volatile int trace_idx;
#define TRACE(id, data) do { \ int i = trace_idx++ & (TRACE_SIZE - 1); \ trace_buf[i].cycles = DWT->CYCCNT; \ trace_buf[i].event_id = (id); \ trace_buf[i].data = (data); \} while (0)각 trace가 5 cycle 정도입니다. 1000개를 모아도 5 KB라 충분합니다.
#SystemView / Segger RTT
Production-grade tracer입니다. J-Link로 수 MB/s 전송이 가능합니다.
#include "SEGGER_SYSVIEW.h"SEGGER_SYSVIEW_RecordEnterISR();// ... ISR workSEGGER_SYSVIEW_RecordExitISR();PC 측에서는 Timeline viewer로 ms 단위로 시각화합니다.
#Hardware Trace (ETM·ITM)
Cortex-M3+의 **Embedded Trace Macrocell (ETM)**은 모든 명령을 trace합니다. J-Trace 같은 비싼 도구가 필요합니다.
**ITM (Instrumentation Trace Macrocell)**은 32 channel debug print를 제공하며, RTT보다 가볍습니다.
ITM->PORT[0].u8 = 'A'; // 1 cycle, non-intrusive#Profile-Guided Optimization (PGO)
# 1. Instrument 빌드gcc -fprofile-generate -o app app.c
# 2. 실 데이터로 실행./app < real_input
# 3. Profile로 최적 빌드gcc -fprofile-use -O3 -o app app.c컴파일러가 hot path를 우대해서 처리합니다. Branch prediction과 inline 결정이 개선됩니다.
#자주 하는 실수
⚠️ Wall-clock으로 CPU 시간 측정
DMA와 context switch가 포함되면서 코드 자체의 시간이 과대 추정됩니다.
⚠️ DWT_CYCCNT init 누락
DEMCR.TRCENA를 활성화하지 않으면 CYCCNT는 항상 0입니다.
⚠️ Out-of-order CPU에서 단순 RDTSC
명령이 reorder되면서 측정 영역 밖에서 실행됩니다. rdtscp와 memory barrier로 해결합니다.
⚠️ printf로 timing 측정
printf 자체가 ms 단위입니다. 그래서 Trace buffer나 GPIO를 씁니다.
#정리
- Wall-clock, CPU cycle, instruction은 각각 다른 답을 제공합니다.
- DWT_CYCCNT(Cortex-M3+)가 µs 측정의 표준입니다.
- Linux에서는
CLOCK_MONOTONIC_RAW와 PMU를 씁니다. - GPIO + 로직 분석기가 외부 정밀 측정 수단입니다.
- Trace buffer로 lightweight production tracing을 구현합니다.
다음 편은 통계적 분석입니다. percentile과 histogram을 다룹니다.
#관련 항목
Embedded Performance Engineering · 4 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 처리량 회복
관련 글
Bare-metal 프로파일링 — GPIO·DWT·SysTick·ITM 활용
OS도 perf도 없는 환경에서 GPIO, DWT cycle counter, SysTick, ITM으로 측정하기.
PMU·HPM 하드웨어 카운터 분석 — 정밀 성능 진단
ARMv8 PMU 6+ counter, RISC-V HPM. CYCLE·INST_RETIRED·CACHE·BRANCH. perf 활용.
실전 사례 — CXL.mem 추가로 LLM inference KV cache 처리량 회복
70B 모델 KV cache가 HBM 한계를 넘어 throughput이 무너졌을 때, CXL.mem 256 GB pool 추가로 회복한 실전 케이스.