Scheduler Latency 측정 기법 — GPIO Toggle·DWT·ftrace·cyclictest
#한 줄 요약
“Scheduler latency는 ISR이 끝난 시점부터 task가 실행을 시작하기까지의 시간입니다.” Hard real-time에서 의미가 있는 값은 평균이 아니라 worst-case입니다.
#Latency 구간 정의
External event (IRQ trigger) ↓ t1: HW interrupt 인지 ↓ t2: ISR 첫 줄 ↓ t3: xSemaphoreGiveFromISR 호출 (task wake signal) ↓ t4: ISR 끝, PendSV trigger ↓ t5: PendSV handler 진입 ↓ t6: vTaskSwitchContext (next task 결정) ↓ t7: 새 task 첫 줄
Interrupt latency = t2 - t1Scheduler latency = t7 - t4Total wake latency = t7 - t1#GPIO Toggle 방법 — Bare-metal·간단
// ISRvoid TIM1_IRQHandler(void) { GPIO_SET(DEBUG_PIN); // t2 (ISR 시작) /* ... */ xSemaphoreGiveFromISR(sem, &woken); portYIELD_FROM_ISR(woken); GPIO_CLR(DEBUG_PIN); // t4 (ISR 끝)}
// Taskvoid rx_task(void *arg) { while (1) { xSemaphoreTake(sem, portMAX_DELAY); GPIO_TOGGLE(DEBUG_PIN); // t7 (task 시작) // ... }}로직 분석기로 GPIO 펄스 폭을 측정합니다. ISR 시작, ISR 끝, task 시작이 세 개의 edge로 나타납니다.
세 edge — t2(ISR 시작), t4(ISR 끝), t7(task 시작) — 사이의 폭으로 ISR 길이와 scheduler latency를 분리해 측정합니다.
#DWT Cycle Counter — Cortex-M
// 초기화CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT = 0;DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
// ISRvolatile uint32_t t_isr_start, t_isr_end;void TIM1_IRQHandler(void) { t_isr_start = DWT->CYCCNT; /* ... */ t_isr_end = DWT->CYCCNT;}
// Taskvoid task(void *arg) { while (1) { xSemaphoreTake(sem, portMAX_DELAY); uint32_t t_task = DWT->CYCCNT; uint32_t sched_latency = t_task - t_isr_end; log_max(sched_latency); }}1 cycle 단위로 측정합니다. 168 MHz면 약 6 ns의 해상도입니다. worst-case 값만 보관해 두고 주기적으로 로그를 남기면 충분합니다.
#ftrace — Linux RT 환경
# Function tracerecho function > /sys/kernel/debug/tracing/current_tracerecho 'schedule:*' > /sys/kernel/debug/tracing/set_ftrace_filterecho 1 > /sys/kernel/debug/tracing/tracing_on
# 실행 후cat /sys/kernel/debug/tracing/traceLinux kernel의 모든 function entry/exit를 추적합니다. schedule 호출 시점을 살펴볼 수 있습니다.
#irqsoff Tracer
가장 길게 interrupt가 disable되어 있던 구간을 자동으로 추적합니다.
echo irqsoff > /sys/kernel/debug/tracing/current_tracerecho 1 > /sys/kernel/debug/tracing/tracing_onsleep 60cat /sys/kernel/debug/tracing/trace | head -50# CPU# TASK DELAY FUNCTION 0) | 120 us __schedule() 0) [worst case] ^^^ — 120 µs IRQ disabled#cyclictest — Linux PREEMPT_RT
sudo cyclictest -p 80 -t 4 -i 1000 -l 100000 -m옵션:
-p 80priority (real-time)-t 44 thread-i 10001000 µs interval-l 100000loop count-mmlockall (page fault 방지)
출력:
T: 0 (12345) P:80 I:1000 C:100000 Min:5 Avg:7 Max:23T: 1 (12346) P:80 I:1500 C: 66667 Min:6 Avg:8 Max:31여기서 Max 값이 worst-case wake latency입니다. Hard real-time이라면 이 값이 deadline 안에 들어와야 합니다.
#SystemView — Segger 시각화
J-Link와 SystemView app을 사용하면 RTOS event를 실시간으로 트레이스할 수 있습니다. Context switch, IRQ, API 호출이 모두 timeline 위에 표시됩니다.
// InitSEGGER_SYSVIEW_Conf();
// Trace 자동 — FreeRTOS trace macro 활용GUI에서 task 실행 구간과 IRQ 구간을 색으로 구분해 보여 줍니다. Latency 문제를 즉시 식별할 수 있습니다.
#Tracealyzer
Percepio의 비슷한 도구입니다.
vTraceEnable(TRC_START);// 자동 traceRecording을 PC tool로 분석합니다. FreeRTOS, Zephyr, ThreadX 등 RTOS별로 특화되어 있습니다.
#Latency 원인 분류
| 원인 | 추가 latency | 해결 |
|---|---|---|
| Critical section 길게 | ~10-1000 µs | 짧게 분할 |
| BASEPRI mask | ~수 µs | priority 분리 |
| 다른 ISR 처리 중 | 그 ISR 길이 | ISR 짧게 |
| Cache miss (Cortex-A) | ~수 µs | hot path lock·prefetch |
| MMU TLB miss | ~수 µs | hugepage |
| Bus contention | 가변 | priority QoS |
| FPU lazy stacking | 0-1 µs | 자동 |
#Worst-Case 추적 — Statistical
static uint32_t latency_buckets[100];static uint32_t max_latency = 0;
void log_latency(uint32_t cycles) { int us = cycles / (CPU_HZ / 1000000); if (us >= 100) us = 99; latency_buckets[us]++; if (cycles > max_latency) max_latency = cycles;}
// 주기적 출력for (int i = 0; i < 100; i++) { if (latency_buckets[i] > 0) printf("%d us: %u\n", i, latency_buckets[i]);}Histogram과 max를 함께 기록하면 p99, p999, max를 모두 추적할 수 있습니다. 위험한 것은 평균이 아니라 long tail입니다.
#실제 측정 사례
| 시스템 | Avg | p99 | Max | 비고 |
|---|---|---|---|---|
| FreeRTOS Cortex-M4 @ 168 MHz | 0.5 µs | 1 µs | 2 µs | Bare-metal |
| Zephyr Cortex-M4 | 0.7 µs | 1.5 µs | 3 µs | |
| Linux mainline | 50 µs | 200 µs | 5 ms | non-RT |
| Linux PREEMPT_RT | 10 µs | 30 µs | 100 µs | RT patch |
| Xenomai | 5 µs | 15 µs | 50 µs | Cobalt core |
| QNX | 3 µs | 10 µs | 30 µs | Hard RT 인증 |
Bare-metal Cortex-M이 가장 deterministic한 결과를 보입니다. Linux는 PREEMPT_RT를 적용해도 100 µs 수준에 머무릅니다.
#자주 하는 실수
⚠️ 평균만 보고 OK라고 판정합니다
Hard real-time에서는 max가 deadline 이내에 들어와야 합니다. 평균이 1 µs라도 max가 1 ms면 그 시스템은 실패한 것입니다.
⚠️ 측정 환경이 실 환경과 다릅니다
Bench에서는 빠른데 실 환경에서는 cache, DMA, bus contention이 더해집니다. 실제 운용 조건에서 며칠 단위로 측정해야 의미가 있습니다.
⚠️ DWT를 켜는 것을 잊습니다
CoreDebug->DEMCR와 DWT->CTRL를 활성화하지 않으면 CYCCNT가 0에 머무릅니다.
⚠️ cyclictest의 priority가 너무 낮습니다
p1 같은 낮은 priority로 돌리면 다른 task에 preempt되어 측정이 부정확해집니다. 최고 priority에 mlockall을 함께 적용해야 합니다.
#정리 — Part 2 마무리
- Scheduler latency는 ISR이 끝난 시점부터 ready task가 실행을 시작하기까지의 시간을 말합니다.
- 측정 방법은 GPIO와 로직 분석기, DWT CYCCNT, ftrace, cyclictest, SystemView가 대표적입니다.
- 평균은 거의 의미가 없습니다. Hard real-time의 진실은 worst-case (max)입니다.
- Bare-metal RTOS는 1-2 µs 수준, Linux PREEMPT_RT는 30-100 µs 수준입니다.
이로써 Part 2 (Scheduler & Context Switch)를 마무리합니다. Part 3에서는 IPC와 Sync 내부 구현으로 넘어갑니다.
#관련 항목
Practical RTOS Internals · 21 of 53
- 1Practical RTOS Internals — 실시간 커널 내부 분석 시리즈 소개
- 2RTOS가 필요한 이유 — 일반 OS와의 결정적 차이
- 3Task와 Thread 개념 — TCB·상태 머신·생명 주기 분석
- 4실시간 스케줄링 알고리즘 비교 — RR·Priority·EDF·RMS
- 5Preemption과 Cooperation — 강제 전환 vs 자발 양보
- 6인터럽트와 RTOS — ISR Context·Deferred Processing·FromISR API
- 7동기화 기초 분석 — Critical Section·Mutual Exclusion·Race Condition
- 8Semaphore 개념 분해 — Counting·Binary·P/V 연산
- 9Mutex 개념 분해 — Ownership·Recursive·Priority Inheritance
- 10큐와 메시지 패싱 — Producer-Consumer·Ring Buffer·전달 의미
- 11실시간성 분석 — Latency·Jitter·Deadline·WCET·RMA
- 12Ready List 자료구조 분석 — Linked List·Bitmap·O(1) Scheduler
- 13Blocked List 자료구조 — Timeout 정렬·Delta List·Two-List Scheme
- 14Scheduler 알고리즘 구현 추적 — Next-Task Selection 로직
- 15Context Switch 원리 분석 — 레지스터 저장·복원·Stack Frame
- 16ARM Cortex-M Context Switch — PendSV·MSP/PSP 어셈블리 추적
- 17ARM Cortex-A Context Switch — Mode 전환·SVC·Banked Registers
- 18RISC-V Context Switch 분석 — ECALL·mret·CSR
- 19RTOS Tick과 타이머 — SysTick·Generic Timer·configTICK_RATE_HZ
- 20Tickless 모드 구현 — Idle Tick Suppression·Sleep·Wake 보정
- 21Scheduler Latency 측정 기법 — GPIO Toggle·DWT·ftrace·cyclictest
- 22RTOS Tracing과 Observability — Tracealyzer·SystemView·ITM/ETM
- 23Critical Section 구현 비교 — IRQ Disable·BASEPRI·Spinlock
- 24Semaphore 내부 구현 추적 — Counter·Wait List·ISR-Safe Variant
- 25Mutex 내부 구현 추적 — Owner·Recursion Count·ISR 금지
- 26Priority Inversion 문제 — Mars Pathfinder 사례·Bounded vs Unbounded
- 27Priority Inheritance 구현 — Inherit·Disinherit·Chain
- 28Priority Ceiling Protocol — Immediate vs Original 비교
- 29Queue 내부 구현 추적 — Ring Buffer·2 Wait Lists·Atomic Send/Receive
- 30Event Group 분석 — Bit Flag·AND/OR Wait·Sync Barrier
- 31ISR-Safe API 설계 — FromISR 패턴·Higher Priority Wake·Deferred Work
- 32Deadlock 분석 — 4 조건·Wait-for Graph·Lock Ordering·Timeout
- 33Stream Buffer와 Message Buffer — FreeRTOS 10의 Lock-Free SPSC
- 34실시간 메모리 요구사항 — Determinism·Fragmentation·WCET
- 35FreeRTOS Heap_1~5 분석 — 5종 Allocator의 구조와 트레이드오프
- 36TLSF Allocator 분석 — Two-Level Segregated Fit O(1)
- 37Static Allocation — 컴파일 타임으로 동적 위험 제거하기
- 38Memory Pool — Fixed-Size Block Allocator의 단순함과 강력함
- 39Stack Overflow 탐지 — Canary·MPU·Watermark 3중 방어
- 40SMP RTOS 설계 — Ready List·Affinity·IPI·Load Balancing
- 41SMP Spinlock 구현 — LDREX/STREX·Ticket Lock·MCS·WFE/SEV
- 42Software Timer 분석 — Daemon Task·자료구조·ISR-Safe API
- 43RTOS System Call — SVC·ECALL·User/Kernel 분리·FreeRTOS-MPU
- 44TrustZone과 TF-M — Secure/Non-Secure·NSC Veneer·PSA
- 45AMP와 OpenAMP — Heterogeneous SoC·RPMsg·remoteproc
- 46C++ in RTOS — RAII·std::thread·ETL·Coroutine
- 47FreeRTOS 소스 분석 — tasks.c·queue.c·port.c 추적
- 48Zephyr 커널 분석 — k_thread·k_sem·Driver Model
- 49RT-Thread 분석 — Object 모델·Components·Smart·Studio
- 50RTOS 포팅 가이드 — 새 아키텍처에 옮기는 절차
- 51RTOS 선택 가이드 — Footprint·License·Certification·Ecosystem
- 52Apache NuttX 분석 — POSIX·PX4·NASA Ingenuity
- 53PREEMPT_RT Linux — Mainline 6.12·Xenomai 4·EVL
관련 글
Scheduler 알고리즘 구현 추적 — Next-Task Selection 로직
FreeRTOS pxCurrentTCB 결정. CLZ 최적화, tie-breaking, scheduler entry points.
Blocked List 자료구조 — Timeout 정렬·Delta List·Two-List Scheme
Blocked task의 timeout 관리. Sorted list + tick wraparound 처리. FreeRTOS의 2-list scheme.
Ready List 자료구조 분석 — Linked List·Bitmap·O(1) Scheduler
Ready 상태 task를 보관하는 자료구조 선택이 곧 스케줄러 latency를 결정합니다. FreeRTOS의 array-of-lists, bitmap + CLZ 최적화, uC/OS의 8×8 LUT까지 한 번에 정리합니다.