系列:《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 篇 · 总纲(阿里云社区)
上一篇:10-2《sparse top-k 块选择:sparse_attn_head 的实现》 | 下一篇:第 11 天《前缀复用》
真机实测通过:本文实验已在 RK3588 板端实测完成(2026-09;方法学与原始记录见仓库 docs 与《实验脚本》目录)
一句话导读:推理引擎里诚实的稀疏边界:短上下文开稀疏是纯负优化,q4 KV、spec 等开关与稀疏互斥——负面清单每条都配源码行号与板端对照,并如实披露 test-sparse 自检崩溃这一已知缺陷。
关键词:手搓 Qwen 推理引擎、千问大模型推理、Qwen3-VL、零依赖纯 C、稀疏注意力、负优化、sparse-attn、边界、RK3588
导语:稀疏并不总是好买卖。本篇是一张诚实的"别开"清单:短上下文开稀疏是纯负优化,q4 KV 与 spec 等开关和它互斥,每条都配源码行号与 RK3588 板端对照,并如实披露 test-sparse 自检崩溃这一已知缺陷,帮你判断手头的场景到底该不该开 --sparse-attn。
前两篇把稀疏讲得像"越大越赚":长上下文省 decode、prefill 的 ATTN 时间降 74%(x86 文档口径)……但任何优化都要配一张"别开"清单。今天这份清单每条都有代码或板端依据,包括我们自己的翻车记录。
1. 知识点:稀疏的成本结构
稀疏不是免费的:它用"少扫 KV"换"多付选择开销"。选择开销包括:
- 探针:每块要抽
n_probe个位置算点积(decode 每词、每头都来一遍); - 重要性:decode 每词要读/累加 prefill 留下的
importance[](O(n) 扫描,第 8534–8541 行的imp_sum归约); - 贪心 top-k:实现是"反复扫全数组挑最大"的贪心,最坏 O(n_blocks²)(第 8587–8619 行两段选择循环);
- 临时缓冲:
probe/used/sel/imp_sum每次调用 malloc/free(第 8513–8531 行)。
这些开销与上下文一起涨(块数变多)。所以稀疏成立的前提是:省下的 KV 扫描 ≫ 多付的选择开销。省不下(短上下文)、或选择开销本来就不用付(有更便宜的替代路径)时,稀疏就是纯开销。
2. 对应代码:什么时候稀疏根本不进场
- 门控门槛:
seq_len > g_sparse_block * 2(decode 第 9176 行 / prefill 第 10805 行)。上下文 ≤64 token:连块都不用切,走精确; - q4 KV 互斥:decode 门控里那个
!st->use_kv_q4(第 9176 行)——--kv-q4时 decode 侧稀疏不可达(main.c 第 4868 行解析--kv-q4); - L3 evict 依赖:
--l3-evict需要--sparse-attn提供"冷块选择信号",两者捆绑(main.c 第 1892 行 warning); - 推测解码互斥:
--spec在稀疏开启时禁用(优化配置与边界说明.md §3.4 边界,!g_sparse_attn才可用)。
3. 改动后果:板端对照 + 诚实的清单
实验一:短上下文(n≈506)开/关稀疏
口径同 10-1(RK3588 / Qwen3-VL-2B / 2026-09 / serve 贪婪 / 相同 prompt 文本,只切开关;n=506 时全上下文只有 16 个块,k=32 意味着"想剪枝却一块都剪不掉")。
| 上下文 n | 精确 prefill | 稀疏 prefill | 精确 GEMM/ATTN | 稀疏 GEMM/ATTN | decode 总耗时(精确 vs 稀疏) | decode attn(精确 vs 稀疏) |
|---|---|---|---|---|---|---|
| 506 | 13.09 s | 13.65 s | 87.8% / 9.0% | 85.8% / 11.1% | 52.5 vs 56.6 ms/词 | 9.7 vs 13.4 ms/词 |
读法:n≈0.5K 时 ATTN 占比 ~9%,注意力本来就不是瓶颈;而 sparse 每词要付探针/重要性/选择的固定成本,且 n_blocks(16) < k(32) 意味着没有任何剪枝——所以 prefill 反而慢 4.3%、decode 每词慢 ~8%。结论:上下文 <1K 的请求开稀疏,是纯负优化。
实验二:为什么长上下文是稀疏的主场(同一开关,翻到 10-1 表 B)
| 上下文 n | 精确 decode attn | 稀疏(k32) decode attn | 精确每词总耗时 | 稀疏每词总耗时 |
|---|---|---|---|---|
| 506 | 9.7 ms | 13.4 ms | 52.5 ms | 56.6 ms |
| 2046 | 48.9 ms | 39.1 ms | 92.3 ms | 82.3 ms |
| 4059 | 110.7 ms | 55.4 ms | 155.2 ms | 100.0 ms |
同一套代码、同一个开关,n≈0.5K 是负优化、n≈2K 小幅受益(decode −11%)、n≈4K 省 ~35% decode 总耗时——"该不该开稀疏"的唯一正确答法是:量你手头的上下文长度。
负面清单(每条给依据):
| # | 场景 | 结论 | 依据 |
|---|---|---|---|
| 1 | 上下文 ≤64 token | 根本不进场 | 门控 2×block(源码 9176/10805) |
| 2 | 上下文 <1K 的短请求 | 无收益(开销≈省下的) | 本板实测(实验一) |
| 3 | 单图多模态(~880 视觉 token) | 无性能收益(质量逐字一致但 ATT 占比低) | 文档第 45 行 |
| 4 | --kv-q4 |
decode 稀疏不可达(互斥) | 门控 !use_kv_q4(9176) |
| 5 | --l3-evict 不带 --sparse-attn |
起不来/告警 | main.c 1892 |
| 6 | --spec 推测解码 |
稀疏开启时禁用 | 文档 §3.4 |
| 7 | k 调低于 32 | 质量下边界失效(10-2 实测 k=1 漏针) | 板端实测 + 文档第 42 行 |
| 8 | prefill 想靠稀疏大幅提速 | 幅度取决于 GEMM/ATTN 占比:x86 8B 的 8K 档 GEMM 主导、总时长只省 ~11%(文档第 185 行);本板 2B 的 4K 档 ATTN 占 59%、实测省 28.6%(表 A)——先量占比再定预期 | 文档第 185 行 + 本板表 A |
一条必须披露的已知缺陷:引擎自带的稀疏单元自检 --test-sparse(main.c 第 4846 行 → vllm_safetensors.c 第 8806 行 st_test_sparse_attn)在板端会崩溃——Day 1-3 已如实记录,2026-09 复测依旧 Segmentation fault(exit 139)。它只影响该自检、不影响推理主路径(主路径另有 10-1/10-2 的实测背书),但它是仓库里未修缺陷的真实样本:看到 PASS/FAIL 自检门红灯,要能区分"主路径坏"与"测试本身坏"。
边界诚实(×3):① 本板实验为 serve 单请求、贪婪、单轮;批量/并发/采样场景另测。② 长上下文质量路径另有与稀疏无关的间歇性 prefill 崩溃记录(文档"已知问题"第 1 条),排查时别把账记到稀疏头上。③ x86 文档表是 8B/16 线程归因数据,与本板 2B 绝对值不可直接对比——本文所有结论以同口径同板对照为准。
4. 学员调试任务
- A 档(板端动手):复测实验一(同 prompt,n≈0.5K,精确 vs
--sparse-attn),记录两组 prefill 总时长与[DEC-SINGLE]attn,验证"短上下文开稀疏不赚"。再把同一 prompt 换成长档(n≈4K)复测,体会"同一个开关,不同长度方向相反"。 - B 档(纯读源码):从 10-1/10-2 的门控与开销代码里,为负面清单第 1、4、5、6 行各找出一处源码证据(行号),并回答:为什么短上下文时稀疏的"选择开销"随块数变多,但省下的 KV 却不成比例?
预期输出:你能为自己手头的场景给出"该不该开 --sparse-attn"的判断,并说出三条以上理由(长度、KV 格式、与 spec/L3 的组合约束)。
收尾
- 本篇源码点名:vllm_safetensors.c(门控 9176/10805、选择开销 8513–8531、
st_test_sparse_attn8806)、main.c(--kv-q44868、--test-sparse4846、L3 告警 1892)、优化配置与边界说明.md(§1 边界、§3.4、§五/§六)。 - 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
- 下篇预告:稀疏解决"单次长请求"的注意力膨胀;但真实服务更常见的浪费是多轮对话——第二轮把第一轮的 prefill 又算了一遍。第 11 天上"前缀复用":怎么让重复的 prefill 一分钱都不花。