系列:《0.8MB 跑通 Qwen:从零实现 ARM 零依赖纯 C 推理引擎》(30 天 × 90 篇) | 适配模型:Qwen3-VL-8B-Instruct(千问3_VL_8B_Instruct)· Qwen3-VL-2B-Instruct · Qwen3-30B-A3B | 测试设备:RK3588(4×Cortex-A76 + 4×Cortex-A55,aarch64)
系列总纲:《0.8MB 跑通 Qwen》30 天 90 篇 · 总纲(阿里云社区)
上一篇:7-2《混合精度路由:flags 决定谁用 Q8》 | 下一篇:第 8 天《注意力与 KV 入门》
真机实测通过:本文实验已在 RK3588 板端实测完成(2026-09;方法学与原始记录见仓库 docs 与《实验脚本》目录)
一句话导读:推理引擎的预热与测量抖动:同一 bench 连跑五次,同一条内核横跨约 1.7 倍,讲清测量抖动的三类来源,以及基准报告为何统一采用重复 3 次取中位的口径。
关键词:手搓 Qwen 推理引擎、千问大模型推理、Qwen3-VL、零依赖纯 C、预热、测量抖动、中位数、性能基准、RK3588
导语:同一台机器、同一个二进制、同一组参数,把
--bench-mixed连跑五次,同一条内核的耗时能横跨约 1.7 倍。手搓 Qwen 推理引擎的基准报告因此把口径写死:预热、多次、重复 3 次取中位。这篇用板端数据量化抖动,讲清它从哪来。
7-2 末尾提醒了:同一个 bench 的数字会抖。这一篇把"抖"量化给你看——同一台机器、同一个二进制、同一个参数,连跑五次 --bench-mixed,同一条内核的时间差能有多大?答案是:±40%。不会读抖动的基准,等于没有基准。
1. 知识点:测量抖动从哪来
一个内核的计时 wall_time 里混着三类成分:
- 真信号:内核本身要花的时间(我们想要的那个);
- 系统噪声:内核态时钟、中断、页面换入、其他核的调度——这些是随机的、改不了;
- 初始化成本:第一次跑某段代码时,分支预测器没热身、TLB 没建立、数据页不在 cache——第一次总是更慢。
所以在"测性能"这件事上:单次读数不可信,第一轮读数尤其不可信,最稳的口径是"预热 + 多次 + 取中位"。中位数比平均值抗离群(一次被中断打断的 6.7ms 不该把平均值抬高)。
2. 对应代码:基准报告的口径
看 RK3588_性能基准报告.md 的表头与脚注,处处是口径声明:
- 冷启动表:"重复 3 次取中位"(2.01 / 2.01 / 2.02 → 中位 2.01);
- prefill 表:每档给出一串样本(如 8K:
202.0 / 203.0 / 202.6)而不是一个数; - 文档开头写死环境:"板内单引擎串行测量(任何时刻只跑一台引擎),无 CPU 抢占;运行环境无网络流量/后台负载"。
先定口径,再跑数——这是性能报告的第一纪律。单点数字("我测了 4.5ms")在论文/营销里毫无意义,因为别人无法判断它是运气还是实力。
3. 改动后果:同一个 bench 连跑五次,看抖动长什么样
在板端把 --bench-mixed 连跑 5 次,只抽同一条内核(Q8 fused gate+up decode)的耗时。板端实测(RK3588 / 2026-09,进程每次冷启动、无预热步骤):
run1 : Q8 fused gate+up (dec) 4.765 ms 42.2 GFLOPS
run2 : Q8 fused gate+up (dec) 4.771 ms 42.2 GFLOPS
run3 : Q8 fused gate+up (dec) 3.940 ms 51.1 GFLOPS
run4 : Q8 fused gate+up (dec) 6.732 ms 29.9 GFLOPS
run5 : Q8 fused gate+up (dec) 4.700 ms 42.8 GFLOPS
排序:3.940, 4.700, 4.765, 4.771, 6.732 →
- 中位数 4.765 ms(42.2 GFLOPS 档);
- 但单次读数横跨 3.94 → 6.73 ms,最差/最好相差 1.7 倍(围绕均值约 -21% / +35%)。
如果你只跑一次就报数:报 3.94 是"运气最好的一次",报 6.73 是"被系统踢了一脚的一次"——两个都不能代表内核。这也能解释本系列前几篇里同一个 bench 在不同天给出的 GFLOPS 为何不完全一致(6-2 的 51.3 vs 本轮的 42.2):那不是代码变了,是测量抖了。中位数口径(3 次以上)才是可复现的。
一个实用姿势:连跑时观察 run3 之后是否逐渐稳定(预热),正式测量前先"空跑"一轮让 TLB/分支预测器热身。任务里给的流程就是标准做法。
4. 学员调试任务
- A 档(板端动手):把
--bench-mixed连跑 5 次,各抽Q8 fused gate+up (dec)一行,记录 5 个耗时;- 算中位数(排序取中间);
- 观察 min/max 跨度(预期 ≥1.5×);
- 去掉第一次(视为预热)再取中位,比较差异;
- 最终以"3 次中位 + 环境声明(同机串行/无后台负载)"的格式写一条结论。
- B 档(纯读源码):读基准报告第 24–32 行(冷启动)与 38–48 行(prefill),把其中"样本列表 → 中位"的口径抄一遍,理解为什么报告不写"平均"。
预期输出:你能说清测量抖动的三个来源、为什么"预热 + 多次 + 中位"能抗噪声,并看懂本仓库基准报告所有数字背后的口径。
收尾
- 本篇源码点名:RK3588_性能基准报告.md(冷启动/prefill 的样本与中位口径)、
--bench-mixed(板上可复现的抖动样本)。 - 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
- 下篇预告:量化与矩阵都过关了,可"算得对、算得快"之外,还有一层数学在等着——注意力。第 8 天我们从 Q·K^T / softmax / V 开始,看它为什么是数值危险区。