33 · PSRAM 只有 1.31×:把输出头拆给两个核,以及先修好带宽仪表
本篇对应源码:
main/kmcu.c·main/main.c·docs/optimization_plan.zh-CN.md
目标:输出头占单 token 的 43%,能不能用第二颗核把它砍一半。
结论:双核聚合带宽只有 1.31×(不是 2×),输出头 91.8 → 82.3 ms,端到端 −5%。
关键词:双核 · PSRAM 带宽 · 输出头 · 分核
封面:双核只有 1.31×,输出头 91.8 → 82.3 ms
一、结论先行
| 项 | 单核 | 双核 |
|---|---|---|
| 输出头扫描(正式模型,20 次均值) | 91.82 ms | 82.28 ms(1.12×) |
| 稳定 decode | 178–188 ms/token | 170–173 ms/token(5.8 tok/s) |
| Agent 8 步 | 1739–1755 ms | 1677 ms |
| top-8 / 全量 logits 哈希 | — | MATCH / 一致 |
收益只有 1.12×,因为瓶颈不在核数,在 PSRAM 带宽。
二、先修仪表:原来的探针测的是延迟
最初的带宽探针是:
for (i = 0; i < n; i++) sum += r[i]; /* 单链依赖 */
sum += r[i] 每一步都依赖上一步,测到的是加法延迟,不是读带宽。换成
8 路独立累加后数字立刻变了:
| 探针 | 结果 |
|---|---|
| 朴素单链 | 106–107 MB/s |
| 8 路独立累加(单核) | 120–122 MB/s |
| 双核各读 8 MB、同时进行 | 157 MB/s(core0/core1 各 101.3 ms) |
双核 1.31×,不是 2×。这直接决定了后面所有「要不要多核」的判断:
如果按 2× 估算收益,会得出「拆两半就能省一半时间」的错误结论。
三、拆法:按词表区间分核,按 id 升序重放
core0: rows [0, V/2) → 本地 top-k
core1: rows [V/2, V) → 本地 top-k
合并:把 2k 个候选按 id 升序重放,用与单核完全相同的「严格 >」规则
为什么必须按 id 升序重放?单核扫描的隐含规则是「先出现者优先」。
如果直接比较两边的最大值,遇到浮点相等时 Tie-break 会和单核不同,
top-8 顺序会变——虽然 argmax 通常不变,但位级门禁会挂。
/* 合并后按 id 升序,逐条套用单核规则 */
for (int i = 0; i < n; i++) {
float acc = cv[i];
if (acc > best) { best = acc; best_id = ci[i]; }
...
}
四、门禁:哈希路径强制单核
if (km_head_dual_enabled && out_hash == NULL && vocab >= 1024) { ...双核... }
out_hash != NULL 说明这次调用要输出「全量 logits 的哈希」用于位级对拍——
双核路径只保留 top-k,拿不到全量,所以这种情况直接走单核。
这不是为了正确性(top-k 双核本身是对的),而是为了让对拍路径保持可比的实现。
五、几何探针:先量天花板,再改代码
正式模型不方便反复加载(每次开机展开 86 秒),所以做了一个不依赖模型文件的
几何探针:在 PSRAM 里构造 32002 行 × 10 块的真实形状,用真实内核扫描。
| 扫描方式 | 时间 |
|---|---|
| 单核 | 89 ms |
| 双核(按区间分核) | 79 ms(1.13×) |
| 双核(行交替分核) | 79 ms(1.13×) |
两种分法一样快,说明瓶颈不是负载不均,而是共享的 PSRAM 总线。
这一步把「双核能拿多少」在上真模型之前就锁死了。
六、为什么不做 attention(E-1 判否)
分桶显示 attn 只有 3.4 ms,占 2%。「单遍 QK」这种改写要动归约顺序,
按规则必须重新对拍,收益上限 3.4 ms——投入产出比不成立,直接出局。
七、诚实边界
- 1.13× 是几何合成的结果,上真模型后是 1.12×,一致性很好,但两者不能混用。
- 双核路径每次调用要创建/销毁一个任务(20 次/token),这部分开销已包含在
170–173 ms 里;若将来 token 更短,任务开销占比会上升。 - 收益与上下文长度无关(输出头不吃 KV),但会随词表增大而线性放大。
八、一句话
先证明双核只能拿到 1.31× 的带宽,再把输出头按词表拆开——预期 2×、
实测 1.12×,端到端 −5%;同时得到一个可复用的结论:这台机器上,任何
「多核并行读 PSRAM」的方案都别指望超过 1.3 倍。
对应源码
| 文件 | 关键符号 / 位置 | 支撑本文哪部分 |
|---|---|---|
main/main.c | probe_psram_dual() / bw_read() | 带宽仪表修正(106 → 121/157) |
main/kmcu.c | head_from_hidden() 双核分支、km_head_job_body() | 按区间分核与升序重放 |
main/main.c | KMCU_HEADBW_PROBE 几何探针 | 89 → 79 ms 的上限测量 |
main/kmcu.c | km_head_dual_enabled | 运行期逃生舱 |
《Kestrel-MCU 手记》 · 全系列 37 篇:在 ESP32-P4 上从零训练并部署 64M 参数 MoE 大模型(5.8 tok/s),源码、权重、训练脚本与全部文章开源可复现。
仓库:https://gitee.com/pei-xiaoguang/kestrel-llm-mcu · 觉得有用欢迎 Star
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_55790517/article/details/169231587




