11 · 性能全景(ARM64):构成模型、微基准、负载矩阵与测量
结论:RT 的收益只体现在「最坏延迟」一个维度——ARM64 上 cyclictest 最大值从非 RT 的数百 µs 到数 ms,降到调优后的 5–50 µs(Neoverse/A76+)或 30–100 µs(A53/A55);平均延迟持平甚至略差。吞吐则按机制退化:CPU 密集几乎无损,锁/中断/softirq/分配密集负载下降 10%–40%。对自动驾驶负载:控制/规划回路受益最大,传感器接入簇付出最多吞吐,感知(GPU/NPU 绑定)基本不受影响。本章先给延迟的构成模型,再逐机制给微基准量级,然后是负载矩阵与延迟预算,最后是复测方法。所有数字除注明外均为公开测试/文献的示意量级,需按 11.6 在目标板实测。
11.1 延迟的构成模型
一次「事件 → 高优先级线程开始执行」的延迟由六段相加:
| 段 | 内容 | 非 RT | RT | 决定因素(ARM64) |
|---|---|---|---|---|
| ① 硬件中断延迟 | 设备 → GIC → CPU 收到异常 | 相同 | 相同 | GIC 优先级、当前 PSTATE.I/PMR、WFI 唤醒、深度 idle 退出(PSCI,100–300 µs) |
| ② hardirq 停留 | 从异常入口到 handler 结束 | 驱动 handler 全部 + softirq 循环(10 µs – 2 ms) | 唤醒桩(几 µs) | 第 03/04 章;伪 NMI 入口序列;Spectre-BHB 入口循环 |
| ③ 不可抢占区剩余 | 被中断的代码若在关抢占区,要等它结束 | spinlock 临界区 / RCU 读区 / bh 段(10 µs – ms) | raw 区 / 显式关抢占(< 10 µs) | 第 01/02/07 章 |
| ④ 调度决策 | need_resched → __schedule() 选核选任务 |
相同 | 相同(TTWU_QUEUE 关,少一次 IPI 延后) |
运行队列长度、RT push/pull |
| ⑤ 上下文切换 | 保存/恢复寄存器、切页表、TLB/缓存 | 相同 | 相同 | A53 3–5 µs,A72 2–3 µs,A78 1–1.5 µs,N2 ~1 µs;SVE 状态、KPTI 页表切换额外 |
| ⑥ 冷启动 | 目标线程的 L1/L2 冷、TLB 冷 | 相同 | 略差(更多切换) | 分区可减少 |
RT 只压缩 ②③,但 ②③ 恰恰是最坏情况的主导项;①⑤⑥ 靠固件/分区/绑核处理。分区 + RT 后,控制线程的最坏延迟 ≈ ① + arch timer hardirq + ④ + ⑤,A78 上通常 10–30 µs。
11.2 各机制微基准(ARM64,示意)
| 操作 | A53/A55(1.2–2 GHz) | A72/A76/A78(1.8–2.5 GHz) | Neoverse N1/N2 | 说明 |
|---|---|---|---|---|
spin_lock+unlock 无争用(非 RT) |
25–40 ns(LL/SC) / 15–25 ns(A55 LSE) | 20–35 ns(A72 LL/SC) / 8–15 ns(A76+ LSE) | 6–12 ns | ARM64_USE_LSE_ATOMICS 决定 CAS vs LL/SC |
rt_spin_lock+unlock 无争用(RT) |
80–150 ns | 40–80 ns | 25–50 ns | 2× cmpxchg + migrate_disable/enable + RCU 进出 |
| 争用锁一次睡眠/唤醒(RT) | 15–30 µs | 5–10 µs | 3–6 µs | 两次切换 + PI 链 |
local_irq_disable/enable 对 |
2–5 ns(DAIF) | 2–5 ns;伪 NMI 开启后 10–30 ns(PMR + sync) | 同 | irqflags.h:38-39, 67-68 |
preempt_disable/enable 对 |
~1–2 ns | ~1 ns | ~1 ns | 计数 |
migrate_disable/enable 对 |
20–40 ns | 10–25 ns | 8–15 ns | 函数调用 + rq 计数 |
local_lock/unlock(RT) |
100–180 ns | 50–100 ns | 30–60 ns | = rt_spin_lock + migrate |
kmalloc(64)+kfree |
60–120 ns → RT 150–300 ns | 40–80 ns → RT 100–200 ns | 30–50 ns → RT 70–120 ns | 无锁快路径失效 |
系统调用 getpid |
300–600 ns | 150–300 ns | 100–200 ns | +KPTI(A75)≈ +300 ns;+Spectre-BHB ≈ +30–80 ns |
| 上下文切换(单次) | 3–5 µs | 1–3 µs | ~1 µs | perf bench sched pipe 一次往返 = 2 次切换 |
| 中断 → irq 线程开始执行(RT) | 8–15 µs | 3–6 µs | 2–4 µs | 唤醒 + 切换 + 冷缓存 |
| softirq 线程化每包额外开销(RT) | +1–2 µs | +0.5–1 µs | +0.3–0.6 µs | 线程切换分摊到批处理 |
| hrtimer 唤醒最大偏差(RT 任务,HARD,满负载) | 20–60 µs | 5–20 µs | 3–10 µs | 含 ① 与 ⑤ |
| 非一致性 DMA 缓存维护 | 0.3–0.5 ms/MB | 0.1–0.2 ms/MB | 0.05–0.1 ms/MB | dcache_clean_poc 逐行 |
| CMA 分配 4 MB(碎片化后) | 1–10 ms | 0.5–5 ms | 0.3–3 ms | 迁移页面 |
| PSCI 深度 idle 退出 | 100–300 µs | 50–200 µs | 30–100 µs | 固件决定 |
读法:RT 把「不可抢占区」从毫秒级压到微秒级,代价是每个锁/分配/中断操作贵 2–4 倍;这些操作在热路径里的密度决定吞吐损失。
11.3 吞吐影响细分(ARM64)
| 负载 | RT 相对非 RT | 主要机制 | ARM64 备注 |
|---|---|---|---|
| 纯用户态计算(矩阵、编解码) | 97%–100% | 抢占更频繁(无 LAZY) | 大核簇几乎无差 |
| 内核编译 / 混合 | 90%–98% | 分配器 + 锁 | 64K 页可略补 |
| hackbench / 管道 | 65%–90% | 切换 + rt_mutex 争用 | A53 端更差(切换贵) |
| 网络小包 pps(雷达 UDP、CAN 网关) | 60%–90% | 线程化中断 + softirq 线程 + backlog 线程 + busylock + 无软合并 | 硬件合并、GRO、busy_poll 可回到 85%+ |
| 网络大包带宽(10 GbE 回放) | 85%–98% | 每包开销被摊薄 | |
| NVMe / UFS 高 IOPS | 70%–95% | 中断线程化 | 记录进程本就在 housekeeping |
| kmalloc/kfree 密集(DDS 序列化、skb) | 70%–90% | 无锁快路径失效 | 预分配/对象池可规避 |
| GPU/NPU 推理吞吐 | 95%–100% | 加速器绑定;驱动中断线程化 +几 µs/帧 | 前提:驱动 RT 兼容 |
| 相机流水线(ISP + V4L2) | 85%–98% | 每帧中断线程化 + DMA 缓存维护不变 | 缓存维护是主项,与 RT 无关 |
| 日志 / 记录写盘 | 80%–95% | I/O 中断线程化 + 分配 | nice 10 + housekeeping |
11.4 自动驾驶负载矩阵
| 负载 | 特征 | RT 对最坏延迟 | RT 对吞吐 | 关键机制 | 放置 |
|---|---|---|---|---|---|
| 控制回路 100 Hz–1 kHz | 单线程、周期唤醒、微秒级抖动要求 | ms → < 50 µs(核心收益) | 无影响 | 01/02/05 | 隔离簇,FIFO 90 |
| 规划 10 Hz | 计算密集、ms 级 | 抖动大幅收敛 | ≈ 无 | 01 | 隔离簇,FIFO 80 |
| 定位 / 融合 | 多传感器时间对齐 | 时间戳抖动下降 | ≈ 无 | 05 | 隔离簇或接入簇 |
| 感知推理 | GPU/NPU 提交与等待 | 提交延迟更稳,帧间抖动小 | 95%–100% | 03/06 | 接入簇,FIFO 55 |
| 相机接入(8–12 路) | 高中断率 + 大 DMA | 帧到达用户态更可预测 | 85%–98% | 03/06 | 接入簇,irq 70 |
| 激光雷达 / 雷达 UDP | 50k–200k pps | 到达抖动下降 | 60%–90%(调优后 85%+) | 04 | 接入簇,napi 60 |
| CAN-FD | 数千 pps,控制相关 | 帧到控制线程 < 100 µs | ≈ 无 | 03 | 控制核,irq 92 |
| DDS / ROS 2 中间件 | 几十线程、UDP 多播、序列化分配 | 关键流抖动下降 | 80%–95% | 04/06 | housekeeping + 关键流分级 |
| 记录 / 上传 / 诊断 | 大块 I/O、周期性 | 不再干扰其它负载 | 80%–95% | 03/06 | housekeeping,nice 10 |
11.5 延迟预算示例:100 Hz 控制回路(10 ms 周期)
| 阶段 | 预算 | RT + 分区后的典型值(A78) | 主要来源 |
|---|---|---|---|
| CAN 帧到达 → irq 线程(FIFO 92) | 30 µs | 5–15 µs | ① + hardirq 桩 + 切换 |
| irq 线程 → 控制线程唤醒(FIFO 90) | 30 µs | 5–15 µs | 调度 + 切换 |
| 控制计算 | 2 ms | 0.5–1.5 ms | 算法;缓存冷启动 |
| 指令发出(CAN 发送 / 共享内存) | 30 µs | 5–20 µs | 驱动路径;TSN 时看门控 |
周期唤醒抖动(clock_nanosleep) |
50 µs | 5–20 µs | HARD sleeper + ① |
| 合计抖动上限 | < 100 µs | 20–60 µs | 满足 |
非 RT 未分区时,同一回路的抖动上限由「最长 softirq/驱动路径」决定,常见 0.5–5 ms,无法满足。
11.6 复测方法
前提
- 两个内核同一 config 基线,只差
PREEMPT_RT(非 RT 侧建议PREEMPT_DYNAMIC+preempt=full作为最接近的对照)。 - 关闭
LOCKDEP、DEBUG_PREEMPT、KASAN、ARM64_DEBUG_PRIORITY_MASKING等调试项。 - 固定频率(
cpufreq.default_governor=performance或cpupower frequency-set -g performance),关闭深度 idle(cpuidle.off=1或 sysfs 逐态disable),两侧一致;big.LITTLE 把测试线程钉在同一类核。 - 每个测试跑到最坏值稳定:cyclictest ≥ 1 小时,验收 24 小时;背景负载要覆盖真实场景(传感器回放 + 感知满载 + 记录写盘)。
延迟:cyclictest(rt-tests)
stress-ng --cpu $(nproc) --io 4 --vm 2 --vm-bytes 1G --timeout 3600 &
hackbench -l 100000 -g 20 &
iperf3 -c <peer> -t 3600 -u -b 0 -l 64 -P 4 & # 模拟雷达小包
cyclictest -m -Sp90 -i200 -h400 -D1h -q > all-cpus.txt # 每 CPU 一线程
cyclictest -m -a8-11 -t4 -p90 -i200 -h400 -D1h -q # 只看隔离簇
看 Max Latencies;直方图尾部决定成败。
尖峰归因:osnoise / timerlat / hwlatdetect
osnoise -a 50 -c 8-11 -d 10m # 谁打断了隔离 CPU(NMI/IRQ/softirq/线程)
timerlat top -c 8-11 -a 50 -T 50 # IRQ 延迟 + 线程延迟拆解,超 50µs 停下打印栈
hwlatdetect --duration=1h --threshold=20 # 固件/安全世界停顿(内核 trace 看不见)
trace-cmd record -p function_graph -e irq -e sched --max-graph-depth 3 cyclictest -m -p90 -i200 -D60 -b50
吞吐
perf bench sched pipe -l 1000000 # 上下文切换(需 arm_pmu;无 PMU 时用 -l 结果的 wall time)
perf bench sched messaging -g 20 -l 10000
hackbench -l 100000 -g 20
iperf3 -c <peer> -t 60 -P 8 # 大包
iperf3 -c <peer> -t 60 -u -b 0 -l 64 -P 8 # 小包 pps(看 lost/total)
fio --name=r --rw=randread --bs=4k --iodepth=64 --numjobs=4 --time_based --runtime=60 --filename=/dev/nvme0n1 --direct=1
# 自动驾驶专项:相机流水线帧率与丢帧、雷达 UDP 丢包、GPU 提交延迟分布(厂商工具 / 自定义探针)
每项重复 5 次取中位数,两侧内核交替跑避免热漂移。
锁、切换与中断分布
perf lock contention -a -- sleep 30 # RT 上显示 rt_mutex 争用
perf stat -e context-switches,cpu-migrations -a sleep 30
cat /proc/interrupts # 隔离列应只剩 arch timer;IPI 被 IRQ_HIDDEN 隐藏
ps -eo pid,cls,rtprio,psr,comm | grep -E 'irq/|ksoftirqd|rcuc|napi/|pr/'
一键脚本
doc/tools/bench/rt-bench.sh run [minutes] 按上述顺序跑完并写到 results/<uname -r>/,rt-bench.sh compare A B 出表。
11.7 解读要点
- 最大值 vs 平均值:只有最大值有意义;RT 的平均值可能略差。
- 尖峰 > 100 µs 且 RT 内核:ARM64 上先
hwlatdetect抓固件级停顿(EL3/安全世界、SCMI、SDEI/RAS),再查 PSCI 深度 idle 与 DVFS,再查 DMA 缓存维护 / CMA / SMMU(第 06/13 章),最后才是内核。 - 尖峰只在某个 CPU:中断亲和没设对,或 per-CPU 内核线程(
kworker、ksoftirqd)被高优先级唤醒。 - 吞吐差距 > 30%:
perf lock contention找被反复睡眠/唤醒的 rt_mutex;考虑 per-CPU 化、批处理,或把对应中断/线程赶到 housekeeping。 - 感知帧率下降但控制回路达标:符合预期——接入簇用吞吐换控制簇的确定性;用硬件中断合并与批处理补回。
小结
- 延迟 = 六段相加,RT 只压缩「hardirq 停留」与「不可抢占区剩余」,但那是最坏情况的主导项。
- 微基准量级:锁/分配/中断操作贵 2–4 倍,A53 端绝对值更高;LSE、KPTI、伪 NMI、DMA 缓存维护是 ARM64 特有的变量。
- 自动驾驶负载:控制/规划最受益,接入簇最吃亏,感知基本中性;100 Hz 回路的 < 100 µs 抖动预算在 RT + 分区下可达成。
- 承上启下:量级明确后,第 12 章给出 ARM64 通用调优,第 13 章落到自动驾驶 SoC 的分区与参数集。