20 · PLE 为什么不加速:每 token 读量才是 MCU 推理的速度真相
English version:en/20-why-ple-does-not-speed-up.md
本篇对应源码:
main/kmcu.c·docs/milestones.md
目标:把「完整配方(MoE + PLE 三值查表 + tied head)落地后,为什么速度没变快」这件事讲清楚。
上一篇 19 只验证了正确性;本篇讲性能归因——PLE 换来的是什么、没换来的是什么。
一、结论先行
PLE 的收益在「容量/质量」,不在「速度」。完整配方每 token 读量不降反略升,
预期速度 ≈ 旧 MoE(甚至略慢)。它解决的是「更大模型装不下」,不是「更快」。
二、速度由什么决定
ESP32-P4 上 decode 是带宽受限(PSRAM ~106MB/s),每 token 耗时 ∝ 每 token 读量。
每 token 读量 = 所有「每 token 必读」权重的字节数。分三类:
| 访问模式 | 读量 | 决定因素 |
|---|---|---|
| 输出头(全扫 vocab×dim) | 最大 | vocab × dim × 量化位宽 |
| attention + MoE 专家(top-k 全读) | 中 | dim × 层数 × 专家激活数 |
| embed 输入 / PLE 查表(读一行) | 可忽略 | 一行 |
三、每 token 读量对比(理论)
| 每 token 读量 | 旧 MoE(独立 lm_head) | PLE 完整配方(tied head) |
|---|---|---|
| 输出头(32002×312 q4 全扫) | ~5MB | ~5MB |
| attention + MoE top-2 + embed | ~0.4MB | ~0.4MB |
| PLE core(新增) | — | ~0.6MB |
| PLE 三值表(查一行 256B) | — | 忽略 |
| 合计 | ~5.4MB | ~6MB |
PLE 模型的每 token 读量不降反升(多了 PLE core ~0.6MB),输出头 5MB 原封不动。
四、关键误区的澄清:tied head 省的是存储,不是读量
很多人(包括我们自己最初的直觉)以为「tied head = 输出头变小 = 更快」。错。
- 存储:tied 复用 embed 当 lm_head,省掉一份独立 lm_head 的5MB flash 存储——这是真的省。
- 读量:输出头每 token 仍要全扫 32002×312的权重(现在扫 embed 而不是独立 lm_head),
读的字节数一模一样——tied改变的是「扫哪个张量」,不是「扫多少」。
所以 tied head 只降低模型体积,不降低每 token 读量。输出头 5MB 全扫的带宽墙原封不动。
五、PLE 换来的到底是什么
PLE 的「容量藏进 flash 查表」换的是容量,不是带宽:
| 维度 | 旧 MoE | PLE 完整配方 | 变化 |
|---|---|---|---|
| 参数量 | 32M | 64M(41M 藏 flash 表) | ↑ 2× |
| 输出头读量 | ~5MB | ~5MB | 持平 |
| 每 token 总读量 | ~5.4MB | ~6MB | ↑ 11% |
| 质量(ppl) | 更高 | 更低 | ↑ |
净效果:容量 ×2、质量提升、速度持平(略慢)。这正是「更大模型又不慢」中「不慢」的真实含义——
不是「更快」,而是「加了 41M 参数,每 token 读量几乎没涨」(因为加在每 token 只查一行的表上)。
六、要真正「加速」,得动输出头
18 篇 已给过对标:PFor 快,是因为它把输出头做成了
tied + 三值 + dim192 = 1.26MB,而我们 tied + q4 + dim312 = 5MB(4× 差距)。
所以真正加速的杠杆只有一个:缩小输出头的每 token 全扫字节数=vocab × d_model × 位宽。三条路:
| 手段 | 效果 | 现状 |
|---|---|---|
| 缩小 d_model | 直接压 head | 舍不得(dim312 是质量底线) |
| 缩小 vocab | 直接压 head | 舍不得(Mistral 32002) |
| head 三值化(1.6bit) | 压 2.5× | 全模型三值判否(match 0.222,15 篇) |
三条路我们当前都主动放弃了(为了质量/词表),所以完整配方不加速是设计选择,不是失败。
七、结论
- PLE 不加速:每 token 读量 ~5.4MB → ~6MB,不降反略升;输出头 5MB 全扫是瓶颈,PLE 没动它。
- tied head 只省存储不省读量——这是最容易被误解的点。
- PLE 换的是容量(41M 参数藏 flash 表,每 token 只多读 256B)→ 质量提升,速度持平。
- 要加速只能动输出头(d_model/vocab/三值化),但三条路当前都因质量/词表被放弃。
- 「更大模型又不慢」的准确含义:加了 41M 参数,速度几乎不涨——这是「不慢」,不是「更快」。
最终速度数据仍需板端实测(PIE 汇编 + 真实带宽墙),PC 标量对拍测不出速度;
但理论读量已能定性判断:预期 ≈ 旧 MoE 的 6.4 tok/s 量级,略慢。
对应源码
| 文件 | 关键符号 / 位置 | 支撑本文哪部分 |
|---|---|---|
main/kmcu.c | km_decode_step() | 二、速度由每 token 读量决定(常驻路径) |
main/kmcu.c | km_decode_step_stream() | 三、每 token 读量对比(流式路径) |
main/kmcu.c | km_fast_build_ex() | 四、tied head 只改展开策略、不改读量 |
docs/milestones.md | M3-7 | 二、逐层流式实测:读量即带宽瓶颈 |
docs/milestones.md | M3-11 | 六、缩小输出头(剪枝/量化)被判否 |
《Kestrel-MCU 手记》· 全系列 28 篇:在 ESP32-P4 上从零训练并部署 32M~64M 参数 MoE 大模型(5.7~6.4 tok/s),源码、权重、训练脚本与全部文章开源可复现。
仓库:https://gitee.com/pei-xiaoguang/kestrel-llm-mcu · 觉得有用欢迎 Star