news 2026/9/25 11:46:25

Kimi K3 in C 调优指南:trunk 优先的内存分配、preset 选择与噪声下限

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kimi K3 in C 调优指南:trunk 优先的内存分配、preset 选择与噪声下限
  • 人工智能
  • 大模型
  • 推理引擎
  • 本地部署

【免费下载链接】kimi-k3-in-c

A 2.78-trillion-parameter Kimi K3 running inference on a single CPU in 8.24 GB of RAM. Portable C99: no BLAS, no framework, no GPU.

项目地址:https://gitcode.com/gh_mirrors/ki/kimi-k3-in-c
点击查看免费下载

本文围绕 docs/TUNING.md 展开,讲清 kimi-k3-in-c 引擎调优中唯一真正重要的决定:把有限的内存预算分给 dense trunk 环缓冲,还是分给 routed-expert 缓存。读完你将掌握:如何用 33% 噪声下限约束调优结论、为什么固定预算下"trunk 优先"能带来约 1.69× 的加速、六个 preset 档位的取舍,以及--incremental、--trunk、--layers等关键开关的底层行为与适用前提。

一、三句话速览

原文档给出的最短版本,也值得全文复述:

  1. 先运行./scripts/k3-doctor.sh,使用它点名的 preset。
  2. 如果你手动调参:先填满 trunk,再喂 expert cache。
  3. 除非你正在验证"全量重算"的等价性,否则一律加--incremental。

scripts/k3-doctor.sh 会依次回答三个问题:工具链是否存在、引擎能否构建;机器有多少内存、对应哪个 preset;权重将要流式读取的存储有多快。它是 Linux-only(读取/proc/meminfo、使用 GNUstat/df、依赖O_DIRECT),流式引擎本身同样只在 Linux 上运行。

二、比其它一切更重要的决定:一 GB 给 trunk 还是给 cache

引擎把内存预算切给两个缓存:

  • --trunk-gb:dense trunk 的环缓冲与 pinned 层预算;
  • --cache-gb:routed experts 的 arena 预算。

这两个数字不可互换,而且不对称性很大。逐 token 来看:

  • 引擎每个 token 都要按固定顺序重读全部 108.81 GB trunk(全部 93 层);
  • 而 routed experts 只读约 25.8 GB,因为每层只选中 896 个 expert 中的 16 个。

所以:

  • 给 trunk 的 1 GB 能移除约 1.17 GB/token 的确定性读流量(钉住一层,之后永不再读);
  • 给 expert cache 的 1 GB,在 arena 低于约 36 GB 时,移除的量测量不到。

2.1 固定 128 GB 预算下的六点扫描

原文档在固定 128 GB 总预算下扫描了 trunk/cache 切分,两端点如下:

trunkcaches/token
12.3110.728.38
110.013.016.80

仅靠分配就获得 1.69×。更快的配置反而拥有更小的 expert cache,expert retention 为 0.0%。

完整 12 点数据在 docs/data/trunk-cache-split.tsv,覆盖 32 GB 与 128 GB 两个独立预算:

totaltrunkcaches/tokenhit%GB read/tokenpeak RSS
322.724.331.230.025.8332.01
326.820.230.580.025.8331.79
3210.816.231.250.025.8331.24
3216.210.830.880.025.8331.90
3221.65.429.130.025.8332.13
3225.61.428.060.025.8332.02
12812.3110.728.3844.0214.46127.89
12830.892.225.2042.9314.74127.53
12849.273.825.6933.9717.06128.14
12873.849.218.3732.2017.51128.19
12898.424.619.460.0025.83127.36
128110.013.016.800.0025.83127.85

读法:128 GB 预算下,一旦 trunk 预算跨过大约 73.8 GB(钉住足够多层),s/token 从 25.7 档跳入 18–19 档;而 12.3/110.7 这种"cache 优先"切分即便 hit 率 44%,GB/token 也降得有限,总速度仍是最慢的。32 GB 预算下所有切分都落在 28–31 s/token,因为总预算太小,分配差异被淹没——这正是原文档提醒的:这些是 33% 噪声下限之上的单样本,读方向,别读精确倍数。

方向性结论由两个预算共 12 点支撑:128 GB 处 Spearman ρ = −0.886,32 GB 处 −0.714,且与上面的机制分析一致;但倍数没有被复现。benchmarks/split-sweep.sh 可以在带重复次的条件下重跑该实验:

$ benchmarks/split-sweep.sh <model_dir> <trunk_dir> <out_dir> [total_gb] [reps]

它保持总内存固定、只扫切分比例(0.10 / 0.25 / 0.60 / 0.86 给 trunk),默认每个切分重复 3 次,依赖systemd-run --scope --user建立真实的内存天花板(Linux-only),输出带逐 rep 的 TSV。与 benchmarks/memory-ladder.sh 的区别在于:ladder 变的是总量,无法区分"内存更多"与"分配更好",而 split-sweep 只变分配,差异可归因于切分本身。完整数据与噪声下限的讨论在 docs/PERFORMANCE.md。

2.2 特例:trunk 与 checkpoint 在不同物理设备上

上面的不对称性有一个前提:trunk 读和 expert 读竞争同一块盘的带宽。如果你的打包 trunk 在一块盘上、checkpoint(routed experts)在另一块盘上,耦合就不成立:trunk 读发生在另一块原本空闲的设备上,几乎免费地与计算重叠,于是环缓冲已经重叠之后,再钉更多 trunk 层收获甚微。

这种布局下真正重要的阈值,是引擎在启动时打印的那个——"第二个环槽变得可负担"的点:

ring held at 1 slot: a second slot needs X.XX GB and the trunk budget is Y.YY GB, so reads are NOT overlapped with compute. Raise --trunk-gb above X.XX GB to enable it.

低于该数字时,trunk I/O 坐在关键路径上,成本可测量地更高;高于它之后曲线变平,继续钉层不再回本。做法是:把--trunk-gb设到略高于该报告值,剩余预算全部给--cache-gb,因为在分体存储布局里expert 设备才是唯一瓶颈。这一结论来自一位将 packed trunk 放在 NVMe、checkpoint 放在独立 SATA 盘的用户实测,完整写见 docs/PERFORMANCE.md。

从源码看,这条消息出自 trunk 流式打开路径的固定打印逻辑(src/io/k3_trunk.c#L469-L479):当请求的环槽数(--trunk-ring N,默认 2)装不进--trunk-gb预算时,环会被压到更少的槽,并提示"Reads are NOT overlapped with compute"(单槽)或"Fewer reads stay in flight"(多槽),同时给出使请求成立的--trunk-gb下限。环槽的代价是实打实的:最低预算下单个槽就是约 2.37 GB,源码注释记录了无条件取第二个槽曾把 laptop preset 从 8.78 GB 抬到 11.12 GB 峰值 RSS(超出 3.0 GB trunk 预算 27%)的教训(src/io/k3_trunk.c#L265-L283)。

2.3 源码印证:trunk 预算为什么真是一个"旋钮"

--trunk DIR启用 trunk 流式后,内存才是可调的。pinned 集合的选取有一个关键设计:按层大小从大到小贪心(而不是最初的前缀序),因为 ring 槽必须容纳"最大的未钉层",前缀序会先钉掉 0 号层(2.34 GB,唯一 dense MLP 层),导致槽白白按它定尺寸、每个预算档位浪费约 1.17 GB。选择是单调的,整个"环槽尺寸 × 钉层集合"的耦合迭代到不动点即收敛(src/io/k3_trunk.c#L288-L330);环境变量K3_PIN_PREFIX=1可恢复旧的前缀序做单二进制 A/B。

对读流量而言,钉哪几层是中性的——总和 B 字节的任意钉层集合,每个 token 恰好省 B 字节;区别全在 ring 槽尺寸上。这就是"--trunk-gb是旋钮"的底层含义:每多给 1 GB,就从最大未钉层开始往下钉,直到预算填满。

三、expert cache 为什么这么弱:是模型设计,不是实现缺陷

K3 的路由器用Quantile Balancing训练,故意把 expert 使用摊平,不让任何 expert 被偏爱。而平坦使用恰恰打穿了 LRU 缓存:没有热子集,几个 GB 留不住任何值得留的东西。实测印证:从 28 个槽到 1,344 个槽,retention 恒为 0.0%,每 token 读的字节一位小数都不动。

它最终还是会生效:arena 大约到 36 GB 时,retention 跳到约 30%,bytes/token 从 25.83 降到 18.11。但那时你本可以用同样的内存钉住 trunk 的大部分,并且更快。

四、Presets:命名的内存预算

presets 存在的目的就是让你不必自己发现 trunk/cache 切分:

$ ./bin/k3 --list-presets
presettrunkcachetotal预期
ultra2.50.31~3 GB仅 proof-of-life;流式读 embed/lm_head,复用 1 个状态槽
laptop31~10 GB~32 s/token
desktop1610~32 GB~31 s/token
workstation6030~96 GB~24 s/token
server11013~128 GB~17 s/token
max110109~224 GB~19 s/token

要点(全部来自原文档与 src/cli/k3_run.c 中K3_PRESETS[]表):

  • server是最佳性价比:trunk 在 110 GB 处已完全钉住,超出部分花在一个贡献甚微的 cache 上。注意在这些测量里max并不比server快——多出的 96 GB 在噪声下限之外什么都买不到。
  • --preset之后的标志会覆盖它,例如--preset server --cache-gb 40是合法的。
  • preset 表中的 trunk/cache 数字是传给两个分配器的预算;而"能不能跑"取决于整个进程的峰值 RSS(含 safetensors 索引、KV 缓存、scratch),源码注释明确说"引用峰值 RSS,不要引用启动横幅里的计划值"(src/cli/k3_run.c#L482-L507)。实测峰值:laptop 8.2 GB、desktop 31.9 GB、workstation 95.5 GB、server ~128 GB、max ~224 GB。
  • ultra是一条刻意独立的执行路径,不是更小的普通 preset:它要求 packed trunk、流式读精确 BF16 的 embedding/lm_head 字节、在全量重算下清空并复用 1 个层状态槽,用 I/O 换 RAM,面向短而确定的 proof 运行;此模式下 speculative/draft 解码会被拒绝。算术、Top-K 路由与权重精度都不变。对应 CLI 开关是--ultra-low-memory(src/cli/k3_run.c#L366-L367)。
  • 另有一个不在表里的auto档:--preset auto(等价--trunk-gb auto)按本机可用内存(MemAvailable)在启动时计算两个预算,trunk 优先,并扣除索引/递推状态/2 GB + 2% 的边量,避免招来 OOM killer;它依赖/proc/meminfo,非 Linux 平台需显式传--trunk-gb/--cache-gb(src/cli/k3_run.c#L1092-L1158)。

五、其它调优选项

--incremental:跨 token 携带 KV 缓存与递推状态

不再重算前缀,而是把 MLA 层的 KV 缓存与 KDA 递推状态在 token 之间传递。已验证与全量重算产生逐 token 相同的输出;除非你在专门测试这个等价性,否则应该用它。

代价是内存:它每个位置分配约 2.37 MBKV 缓存(24 个 MLA 层的 k/v 以 fp32 展开),长上下文很贵——16k 位置约 39 GB。引擎会预先算出需求并带着两个数字拒绝运行,而不是让你一小时后被 OOM killer 收割(src/cli/k3_run.c#L1353-L1361)。KV 缓存是内存计划里唯一随上下文增长的项,因此也是真实上下文上限(src/cli/k3_run.c#L1429-L1432)。

--trunk DIR:把内存变成旋钮

启用 trunk 流式;没有它 trunk 完全驻留,内存下限约 115 GB。打包一次即可:tools/pack_trunk.py(或 scripts/pack-trunk.sh)。

--layers N:只绑前 N 层

用于在部分下载上测试机制;输出不是完整模型,引擎会明说。

--trunk-ring N:环槽数(默认 2)

一个槽是正在计算的层,其余是飞行中的读;第三个槽让读线程领先一层、让设备持续忙,但要多花约 2.37 GB,预算装不下时仍以预算为准(见上文 2.2 的启动提示)。

六、存储比你想的重要得多

低预算下引擎每 token 要搬约 135 GB,存储带宽通常才是天花板,而不是 CPU:

  • 把 checkpoint 与 packed trunk 放到最快的本地 NVMe;
  • 网络存储或机械盘会主导一切。设备带宽差 6×,就是这个工作负载的吞吐差 6×——大于本文档里任何调优决定的影响;
  • ./scripts/k3-doctor.sh会实测你的设备,让你知道自己处在哪个区间。

七、线程

OMP_NUM_THREADS默认取核心数。低内存预算下工作负载是 I/O 受限的,一旦 trunk 开始流式,加线程的收益低于直觉。这一点在本引擎上尚未被系统化扫描,计划见 docs/ROADMAP.md。

八、在断定"某个改动有效"之前

同一配置重复运行的实测散布是33%。比这小的差异不是效应。每个实验臂至少跑 3 次;完整流程见 docs/BENCHMARKING.md。

参考文件

文件用途
docs/TUNING.md本文主体文档
docs/PERFORMANCE.md切分扫描全量数据与噪声下限
docs/BENCHMARKING.md基准流程
docs/data/trunk-cache-split.tsv12 点扫描原始数据
benchmarks/split-sweep.sh带重复次的切分扫描重跑脚本
scripts/k3-doctor.sh机器体检:工具链/内存/存储
src/cli/k3_run.cpreset 表、auto 预算、KV 缓存预检
src/io/k3_trunk.c钉层选取、环槽与启动提示
tools/pack_trunk.pytrunk 打包
  • 人工智能
  • 大模型
  • 推理引擎
  • 本地部署

【免费下载链接】kimi-k3-in-c

A 2.78-trillion-parameter Kimi K3 running inference on a single CPU in 8.24 GB of RAM. Portable C99: no BLAS, no framework, no GPU.

项目地址:https://gitcode.com/gh_mirrors/ki/kimi-k3-in-c
点击查看免费下载

相关推荐

上一篇:汽车总线测试终极指南:TSMaster从入门到精通的5个实战场景
下一篇:6GB显存即可运行!FLUX.1-dev FP8量化模型完整指南:让普通显卡也能玩转AI绘画

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 11:42:06

桌面端CRM选型复盘:从Excel数据孤岛到团队高效协作

接手团队的第一周&#xff0c;我翻遍了所有人的工作电脑&#xff0c;发现一个触目惊心的事实&#xff1a;二十多个销售和售后&#xff0c;客户资料分别躺在Excel、微信收藏、纸质便签和各自的手机通讯录里。离职的销售带走了一个大客户的全部上下文&#xff0c;售后服务每天要重…

作者头像 李华