Skip to content

Latest commit

 

History

History
48 lines (38 loc) · 3 KB

File metadata and controls

48 lines (38 loc) · 3 KB

性能

每按键的解码延迟

cnt-dict-tools bench data/cnt.dict data/lm.cntl 20(18~20 个样例 × 20 轮, 本机实测 2026-08-27,trace 关闭):

平均 中位 90% 99%
热(词键缓存命中,= 连续打字的第 2 键起) 83µs 81µs 119µs 190µs
冷(首次进入该输入上下文) 169µs 150µs

测量环境:Intel Core Ultra 5 336H / governor powersave / cargo build --release (trace 关闭;开启 trace 约 +4%)。

绝对值只在同一台机器同一状态下有意义:同一份二进制在降频状态下实测会慢 2~3 倍(74µs → 200µs+)。改动前后要比性能,用 git worktree 建旧版本 交替跑两个二进制看相对差值,别跨时间点比绝对值(见 AGENTS.md)。

关键设计(解码热路径)

都是先埋 span 看数据再改(见 AGENTS.md 的可观测性约定):

  • 词键缓存跨按键:打字是增量的,敲 womenzaigongzuo 的每个字母都会把整个 前缀重新解码,同一批词键(wo/women/zai/gongzuo…)反复查词库。缓存 词键 → 候选词表(含 LM 下标、句首基础分、用户计数)
  • learn() 精确失效:只丢这次动过的键,不整体清空 —— 否则每次上屏后的下一句 都退回冷路径(实测每键 +26%)。真实打字的增量特性也在这里体现:追加一个字母只 新增「以新末尾结束」的少数词键,前面位置的键全部命中,所以连续打字几乎全是热的
  • 每个位置的词键只枚举一次:词键只取决于「位置 + 音节格」,与假设无关; 此前每条假设都重做字符串拼接与词库前缀查询,BEAM=8 就是 8 倍冗余
  • 假设是 Copy 的小结构,路径存在 arena:此前每条假设持有 Vec<(Arc, Arc)>, 每次展开都要克隆整个 Vec(一次堆分配 + 全部引用计数),而每次解码有约 500 次展开
  • bigram 按「行」查:同一前词的后继在 bigram 表里是连续区间。存活假设 (≤ BEAM 条)先定位一次行,其所有后继在行内二分——把「几十次跨 60MB mmap 的 随机二分」压成「一次定位 + 行内小范围二分」。注意行定位本身是两次全表二分, 放到「每次展开」里会反而更慢(实测 +50%)
  • 排序键先算好再排ranked_words 的比较器里曾调 user_count,每次比较都要 抢用户库的锁 + 两次哈希查找,20 个候选就是上百次加锁
  • 词库词零拷贝ranked_words 返回 Cow,词库词直接借 mmap,少一次 String 中转

历史记录(优化前对照)

本轮优化(词键缓存等上述设计)用「旧版/新版交替各跑 3 次」验证: 旧版 714~760µs、新版(热)~200µs —— 同一降频状态下的 3.5x 提升。

语音链路实测见 docs/voice-input.md(bench:平均 88~92ms,RTF 0.016; ctc_beam 去掉多余 softmax 归一化后 20.1ms → 4.9ms)。