云计算百科
云计算领域专业知识百科平台

32 · 三处保序等价优化:目录缓存、共享量化、RoPE 复用

32 · 三处保序等价优化:目录缓存、共享量化、RoPE 复用

本篇对应源码:main/kmcu.c · host_verify.c · docs/optimization_plan.zh-CN.md

目标:把「每 token 重复做但结果必然相同」的事砍掉,同时保证字节级不变。 结论:三项都通过 216 条 dump 对拍;它们不加总耗时承诺,只承诺「不改变任何数」。

关键词:目录缓存 · 共享量化 · RoPE 复用 · 位级对拍 封面:三处改动,216 条 dump 全 MATCH

一、结论先行

编号改的是什么等价条件对拍结果
O-3 张量名查找加 64 槽直接映射缓存 纯查表,不改变算术 216 条 MATCH
O-4 Q/K/V 与 gate/up 共享一次激活量化 同一公式、同一 xq 位模式 216 条 MATCH,prefill 11 项通过
O-5 RoPE 的 cos/sin 按 (pos, i) 复用 同一输入、同一 libm 返回值 216 条 MATCH

它们属于「保序等价」:可以证明不需要重新拟合、不需要重跑参考实现, 只需要证明读到的数和算出的数一个 bit 都没变。

二、O-3:每 token 上百次字符串查找

km_find(m, "L3.e5.down") 是线性扫描张量目录。一层里要查十几次, 每 token 十层,再加上 PLE 分支,量级是「百次 × 两三百个张量名」。

#define KM_FIND_CACHE 64
static struct { const void *base; const char *name; int idx; } s_find_cache[KM_FIND_CACHE];

int km_find(const km_model_t *m, const char *name) {
uint32_t h = hash(name) & (KM_FIND_CACHE – 1);
if (s_find_cache[h].base == m->base && strcmp(s_find_cache[h].name, name) == 0)
return s_find_cache[h].idx;
...线性扫描...
}

km_open() 时整体失效,避免换模型后拿到旧下标。缓存未命中就回退线性扫描, 所以最坏情况只是「没变快」,不会变错。

三、O-4:同一份激活只量化一次

Q、K、V 三个 GEMV 用的是同一个 x。原实现各调用一次 quantize_act(x, dim, xq)——同样的 312 次缩放、同样的 312 次舍入,做三遍。

拆成两个函数后:

const float invS_qkv = 1.0f / quantize_act(h, dim, xq);
gemv_q8_pre(&f->t[idx_q], dim, dim, xq, invS_qkv, q);
gemv_q8_pre(&f->t[idx_k], kvw, dim, xq, invS_qkv, kk);
gemv_q8_pre(&f->t[idx_v], kvw, dim, xq, invS_qkv, vv);

等价性来自「同一个 xq 位模式」:量化是确定性函数,三次调用得到的 xq 和 S 完全相同,所以共享不改变任何结果。MoE 里每个专家的 gate/up 同样共享一份, 但 down 之后 xq 会被下一次量化覆盖,所以跨专家不能共用。

四、O-5:RoPE 的三角函数

RoPE 要算 cos(pos·θ^(-2i/d)),对每个 head 都一样。原实现每个 head 算一次, 12 个头就是 12 倍超越函数调用。

for (int i = 0; i < half; i++) {
float inv = 1.0f / powf(theta, (float)(2 * i) / (float)hd);
float ang = (float)pos * inv;
float cv = cosf(ang), sv = sinf(ang); /* 每个 i 只算一次 */
for (int h = 0; h < n_heads; h++) { ... } /* 所有 head 复用 */
}

等价条件很硬:同一个 pos、同一个 i、同一个 libm 实现,返回值必须逐位相同。 这也是为什么它被允许放在「保序」而不是「需重新对拍」里。

五、门禁:216 条 dump

host_verify –dump 会输出逐层 hidden、K/V、完整 logits、cls、agent 的原始 float 字节, 再由 tools/dump_diff.py 逐条比较。三项改动后全部 MATCH(tied 模型 225 条)。

这不是「文本看起来一样」,而是字节一样。

六、诚实边界

  • 这三项是捆绑一起验证的,没有单独做「只开 O-3」的耗时 A/B。 它们的设计目标是「省掉重复工作且不改变结果」,不是某个具体毫秒数。
  • 目录缓存在「张量名很相似」时有哈希冲突,但冲突只会退化为线性扫描,不影响正确性。

七、一句话

保序等价的优化最舒服的地方在于:它不需要重新验证精度,只需要证明 「同一输入 → 同一字节」;三项改动全部一次通过 216 条 dump 对拍。


对应源码

文件关键符号 / 位置支撑本文哪部分
main/kmcu.c km_find() / s_find_cache O-3 目录缓存
main/kmcu.c quantize_act() / gemv_q8_pre() O-4 共享量化
main/kmcu.c rope_apply() O-5 三角函数复用
host_verify.c · tools/dump_diff.py –dump + 逐条比较 216/225 条门禁

《Kestrel-MCU 手记》 · 全系列 37 篇:在 ESP32-P4 上从零训练并部署 64M 参数 MoE 大模型(5.8 tok/s),源码、权重、训练脚本与全部文章开源可复现。

仓库:https://gitee.com/pei-xiaoguang/kestrel-llm-mcu · 觉得有用欢迎 Star

赞(0)
未经允许不得转载:网硕互联帮助中心 » 32 · 三处保序等价优化:目录缓存、共享量化、RoPE 复用
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!