Colibri Kimi K3 Metal 后端实现全解析:从差距分析到 93/93 层全 GPU 推理
【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri
Kimi K3 是 93 层、2.8T 总参数 / 104B 激活参数的混合注意力 MoE 模型,本文以 Colibri 仓库的 Metal 实现文档 为骨架,完整拆解其 13 个实现阶段:如何通过"CPU 为正确性基准、Vulkan 为 GPU 算法参考、既有 Metal 后端为实现模板"的三重约束,逐步将 93 层全部迁移到 Apple Silicon GPU 上运行,并做到与 CPU 输出逐位一致。读完本文,你将掌握 KDA 状态递归在 Metal 上的两遍无线程组内存核设计、命令缓冲合并技巧、量化 matmul 的格式覆盖,以及一套可复制的"先正确后性能"的 GPU 移植方法论。
Kimi K3 的架构背景与 Metal 目标
Kimi K3 是 Colibri 仓库中继 GLM-5.2(colibri.c)、Olmoe、Inkling 之后新增的引擎族(实现在 c/kimi_k3.c),模型结构上与 DeepSeek 系完全不同,四块新部件决定了移植工作的难度:
- 混合注意力 + 无位置编码:69 层 KDA(Kimi Delta Attention)+ 24 层带门控的 MLA(每 4 层一个,外加最后一层)。模型不使用任何位置编码,位置信息完全承载在 KDA 的因果卷积与衰减状态中。
- KDA 状态空间注意力:每头维护一个
[hd × hd](hd=128)的逐 token 递归状态,状态更新遵循 delta-ruleS = (I − βkkᵀ)·Diag(e^gk)·S + βkvᵀ,是移植中最大的计算瓶颈。 - AttnRes 替代残差流:每层维护
prefix_sum,每 12 层快照一次,用 softmax 混合替代传统残差相加。 - Stable LatentMoE:sigmoid 路由器
[896, 7168]+ 分数校正偏置,top-16 专家选择,专家权重为 MXFP4 量化(QAT 训练),激活函数为 SiTU-GLU。
Metal 实现的最终目标记录在文档开头:93/93 层全部通过 Metal GPU 运行。移植遵循的指导原则是"永远不要发明新算法"——CPU 实现是正确性基准(oracle),Vulkan 实现是 GPU 算法参考,既有 Metal 后端是实现模板,每个新原语都必须通过 CPU-Vulkan-Metal 三方数值一致性校验后才能进入下一阶段。
差距分析与内核复用清单(Phase 1–2)
Phase 1 产出 docs/kimi_metal_gap_analysis.md,为 Kimi K3 的每一个算子记录 CPU 实现、Vulkan 实现和 Metal 实现状态。Phase 2 将 Vulkan 后端与 Metal 后端逐算子对比,给出复用决策表(节选):
| 操作 | Vulkan | Metal | Action |
|---|---|---|---|
| RMSNorm | yes | yes | reuse |
| GEMM | yes | yes | reuse |
| Expert FFN | yes | partial | extend |
| KDA attention | yes | no | implement |
| KV cache | yes | partial | extend |
复用的核心原则是:凡是已有 Metal kernel 的地方绝不重写。按 Action 分类的统计如下:可立即复用 6 项(量化 matmul、MoE block、router、top-K、GEMM、SiLU),需要从大函数中抽取的 5 项(RMSNorm、gate_up、KV cache、attn_absorb、独立 SiLU),需要新写 kernel 的 2 项(MXFP4 matmulfmt=7、dual-GEMV pair),另有 2 项不适用(双 GPU、VRAM 压力管理——Apple Silicon 单 GPU + Metal 自动 residency 淘汰机制天然覆盖)。
差距分析还明确指出:当时 Metal 要达到 24/93 层只需 5 个条件(独立 RMSNorm、w_matmul的 Metal dispatch、SiTU-GLU、embedding 上 GPU、head forward Metal 路径),而达到 93/93 层还需要 KDA 全量 kernel、MXFP4 matmul、res_mix kernel、MLA 全量 forward 这 4 个关键项。
Metal 编写约束:从 CUDA/Vulkan 心智迁移(Phase 3)
Phase 3 与 METAL.txt 一致,总结了移植 kernel 时必须遵守的 Apple GPU 约束,这些约束直接决定了后续所有 kernel 的设计:
- 统一内存、零拷贝:Apple GPU 与 CPU 共享物理内存,不要模仿 CUDA 的 upload/download 模式,优先持久缓冲区、就地计算、最小化 CPU 同步。
- Threadgroup 尺寸:用 32/64/128/256,绝不默认 1024,始终检查
pipeline.maxTotalThreadsPerThreadgroup。 - SIMD group ≠ warp:不要假设宽度 32,使用
simd_sum()、simd_broadcast()、simd_prefix_exclusive_sum()。 - threadgroup 内存有限:大的注意力 kernel 需要重新设计而非直接翻译。
- 缓冲区对齐
alignas(16):参数结构体对齐错误不会崩溃,只会得到错误数值。 - 地址空间严格分离:
device、constant、thread、threadgroup不能混用,禁止跨地址空间指针算术。 - 只读参数用
constant:编译器能生成显著更优的代码。 - 无全局 barrier:用同一命令缓冲上的顺序 dispatch 表达多 pass 依赖。
[[buffer(N)]]与setBuffer(index:)必须精确匹配:索引错位编译通过但结果是垃圾数据。- 管线编译一次、永久复用;11.一个 device 一个 command queue 用终生;12.一个 command buffer 承载多个 kernel,不要制造大量微型 command buffer。
- 存储模式:Shared(简单、统一内存)vs Private(GPU 独占更快,但需显式传输)。
- 编译器激进:数值调试时关闭
-fast-math。 - FP16 存储 + FP32 累加:避免隐式提升导致的寄存器压力和数值漂移。
- 连续访问优先:保留 Vulkan 张量的合并布局。
- 用 Instruments 分析,不要猜。
文档强调的一个经验法则在后续反复出现:Phase 8.4 之前的非融合路径每个 token 创建 5 个 MTLCommandBuffer,每个往返约 150 μs,单 token 调度开销高达 ~750 μs——这正是约束 12"最小化 command buffer"的直接后果。
脚手架:后端选择、dispatch 钩子与特性开关(Phase 4)
Phase 4 只做基础设施,刻意不实现推理:新增后端选择(if (backend == METAL) kimi_forward_metal(...))、dispatch 钩子和特性开关,Metal 入口故意返回NOT IMPLEMENTED,验收标准仅是"后端选择能编译、无推理"。
从 c/kimi_k3.c 源码可以看到这条脚手架的真实形态:
W结构体新增void *metal字段(kimi_k3.c:153),用于持有注册后的ColiMetalTensor*;- 全局开关
g_k3_metal(kimi_k3.c:414)由环境变量K3_METAL触发(kimi_k3.c:998-1004读取 env 并调用coli_metal_init()); kimima_forward_metal()作为未来整体式 Metal forward 的桩函数存在,目前返回 0 表示 NOT IMPLEMENTED(kimi_k3.c:428);w_matmul()内嵌#ifdef COLI_METALdispatch 钩子,Metal 不可用时自动落到 CPU 的matmul/matmul_q/matmul_i4_grouped。
注意:Metal 激活靠编译期-DCOLI_METAL(即make METAL=1)加运行时K3_METAL=1环境变量,不存在--metalCLI 参数。c/Makefile也明确限制METAL=1仅支持 macOS。
稠密算子移植与数值正确性策略(Phase 5)
Phase 5 移植共享后端(backend_metal)已实现的所有稠密算子。共享路径(GLM 引擎,colibri.c)的接线点包括:
coli_metal_gemm()— 全量化格式 GEMM(colibri.c:689)coli_metal_attn_decode()— MLA decode 注意力(colibri.c:2614)coli_metal_layer_decode()— 完整解码层(colibri.c:4824)coli_metal_moe_block{_begin,_end}()— MoE 专家路由与块分发(colibri.c:3584)coli_metal_rtop8()— 串行/并行 top-8 选择(backend_metal.h:125)coli_metal_register/unregister()— metal-everywhere 路径的缓冲注册(colibri.c:1495 附近)
backend_metal.h公开的 API 表(详见 backend_metal.h):coli_metal_rmsnorm()(RMS 归一化)、coli_metal_add()(残差加)、coli_metal_silu_mul()(SwiGLU 激活)、coli_metal_matmul()(tensor 句柄 API 的 GEMM)、coli_metal_gemm()(F32/Q8/I4 分组直连 GEMM)、coli_metal_rtop8()、coli_metal_attn_decode()、coli_metal_layer_decode()、coli_metal_moe_block*()、coli_metal_register()。
Kimi K3 独立路径与共享路径不同:kimima_forward_metal()保持桩状态,实际推理通过kda_forward()/mla_forward()内部的细粒度 Metal dispatch 完成。w_matmul()对fmt=0(f32)、fmt=1(int8)、fmt=4(分组 int4)分别分发到coli_metal_matmul()(kimi_k3.c:433-460)。
数值正确性策略是 Phase 5 的重头戏,双模式设计:
- fast 模式(默认):充分发挥 Metal 能力,保持 token 级输出一致;
- exact 模式:镜像 CPU 的运算顺序以最小化数值漂移;
- CPU 输出永远是真值集合,逐操作追踪 maxAbs(最大绝对误差)与 MAE(平均绝对误差)。
33 个独立算子测试全部通过:RMSNorm(D=1..16384 多形状,S=1..32)maxAbs ≤ 4.77e-7、MAE ≤ 5.87e-8;add(n 覆盖 1 到 262145 的多种尺寸)完全精确匹配(0.00e+00);silu_mul(n=1..131073)maxAbs ≤ 3.58e-7、MAE ≤ 1.37e-8。
MoE 专家执行移植(Phase 6)
Phase 6 的关键洞察是:路由(rtop8 选择)、专家调度与批次分配、token 到专家的 scatter/gather 逻辑在 CPU/Vulkan 上已经存在,直接复用既有 dispatcher,只把计算密集部分移植到 Metal:
- 专家矩阵乘(per-expert GEMM)→
coli_metal_moe_block()(backend_metal.mm) - 专家激活(per-expert SwiGLU)→ 融合进 moe_block 的 command buffer
- 输出累加(加权 scatter-add 回 token 空间)→ 同一次 submit 中融合
接线点:coli_metal_moe_block_begin()(异步两阶段 encode+commit)、coli_metal_moe_block()(同步回退路径)、coli_metal_moe_block_end()(等待、故障检查、scatter-add)。诊断信息通过coli_metal_moe_counts()、coli_metal_moe_times()、coli_metal_moe_kernel_time()暴露,计数器在推理期间打印(colibri.c:5651 附近)。
backend_metal.h中coli_metal_moe_block()的注释详细说明了单命令缓冲批量执行方式:所有专家共享一个 command buffer,~150μs的 Metal launch 延迟按块只支付一次而非每次 matmul 一次;_begin/_end异步变体允许 CPU 在 GPU 计算驻留专家的同时从磁盘加载未命中专家。
KV Cache(Lc/Rc)与 DSA 索引器(Phase 7)
Phase 7a — KV cache 移植
两个基本原语:coli_metal_kv_write()(backend_metal.mm:1249,写 S 行 KV cache:rmsnorm(L) + rope_interleave(R))和coli_metal_kv_clear()(backend_metal.mm:1306,GPU 上清零[from, to)范围的 cache 行)。
共享后端融合路径:coli_metal_attn_decode()用单个 command buffer 完成q_a → rmsnorm → q_b → RoPE、kv_a → latent rmsnorm@pos + krot RoPE@pos(cache 写入)、MLA absorption、o_proj;coli_metal_layer_decode()覆盖完整层(in_ln → attention → residual → post_ln → shared expert → router+top-K)。
缓冲区管理要点:KV cache 用页对齐的falloc()分配(colibri.c:4996-5007),Lc/Rc 缓冲的 Metal 注册/注销在 colibri.c:4985 与 colibri.c:5007,所有 KV 行对 Metal shader 零拷贝可见。测试矩阵覆盖kv_write S=1..8、kv_clear范围与边界、10 token 的 prefill 顺序构建、pos=15 单步 decode 追加、pos=100 大上下文 decode、完整 cache 重置生命周期、3 序列独立 KV 状态。
Kimi K3 独立路径:coli_metal_kv_write()接入mla_forward()做批量化 GPU 写入,coli_metal_kv_clear()接入model_state_reset(),kv_alloc()具备 Metal 感知——max_t未增长时复用既有缓冲,超容量时才释放重建。
Phase 7b — DSA 索引器(Ic + k_idx)
DSA(Dictionary Sparse Attention)每步 decode 只选出 cache 行的 top-K 子集参与注意力打分,运行在独立的index_hd(通常 64–256)维度索引器 cache 上,远小于完整 K/V。Phase 8 的注意力打分消费k_idx(DSA 选出的位置);没有 DSA 时注意力退化为全上下文(正确但更慢),没有k_idx时 Phase 8 也能以旁路模式工作。
新增Cfg字段:index_hd(索引器隐藏维)、index_nh(索引器查询头数)、index_topk(每 decode 行最多选的位置数)、theta(RoPE base,默认 10000)、idx_type[128](每层 DSA 模式:1=full,0=shared)。idx_type在index_hd > 0时对全部 MLA 层默认 full,显式index_layers覆盖。
新增 Mla union 字段(仅 full 层分配):wk/wq/wp三个投影W矩阵、knw/knb键层归一化权重/偏置、Ic索引器 cache[max_t * index_hd]。新增 Model 字段:dsa_nsel(每 slot 的 k_idx 选择计数)、dsa_sel(每 slot 选中的索引)、dsa_scap(分配容量)。
Prefill 路径(CPU,已接入mla_forward()):对每个 token 依次做w_matmul投影 → 就地 RMSNorm →dsa_rope()(前qk_rope维 rope_interleave)→ 写入Ic。Decode 路径的辅助函数dsa_score_single()已就绪:多头余弦距离打分(Σ_h w32[h]·ReLU(qi·Ic[t]/√hd),缩放1/√nh),再经qsort做 top-K 选择产出k_idx[]。CPU 辅助函数包括dsa_rope()、DsaEntry+dsa_entry_cmp_desc()、dsa_score_single()。文档明确标注:DSA 的 Metal kernel(dsa_kv_write、dsa_score)仍为计划状态,且对 K3 而言index_hd == 0恒成立(全上下文旁路,输出逐位一致)。
KDA 注意力移植:本项目最大的一块拼图(Phase 8)
KDA(Kimi Delta Attention)用于 93 层中的 69 层,每层包含:量化投影(q/k/v/g/o)、f32 低秩衰减投影(fa/fb/bp)、conv1d 深度可分离 taps(conv_q/conv_k/conv_v)、每头 L2 归一化,以及一个逐 token 顺序执行的[heads × hd × hd]状态递归循环。配置参数(来自Cfg,K3 实际取值见 docs/kimi_k3.md):kda_proj=12288、kda_heads=96、kda_hd=128、conv_k=4、gate_lb=0.068。
kda_forward()的执行顺序:先批量投影(C 个 token),再逐 token 循环(conv1d+SiLU → 每头 L2 归一化 → 每头状态扫描 → 每头 RMSNorm+sigmoid 门控),最后批量输出投影。状态扫描是核心瓶颈:96 头并行 ×[128×128]矩阵向量运算,单 token 即 96×128² = 157 万次乘加;69 层 × 每 decode 一步,仅状态扫描就有约 1.08 亿次运算。
8.1 Q/K/V 准备(✅ DONE)
量化投影(q/k/v/g)经w_matmul()→coli_metal_matmul(fmt)分发(复用 Phase 5);f32 低秩投影(fa/fb/bp)经新增的k3_matmul_f32()→coli_metal_matmul(fmt=0)分发(见 kimi_k3.c:462-470);输出投影(o)仍走w_matmul()。Kda结构体新增metal_fa/metal_fb/metal_bp三个 Metal tensor 包装(kimi_k3.c:163)。decode(C=1)时 Metal 分发单行 GEMM,收益在于延迟隐藏而非吞吐。
8.2 注意力状态递归 kernel(✅ DONE)
KDA 的"注意力分数"就是每头的状态递归——与 MLA 的 softmax(Q·K) 打分不同,KDA 打分是状态空间转移s_new = alpha·s_old + kn ⊗ vt。状态跨 token 顺序依赖,但在头与状态维度上完全并行。kernelk3_kda_state调度[H, hd]个 threadgroup(每头一个 threadgroup、hd个线程),每个线程处理[hd × hd]输出空间的一个(i, j)元素。签名(backend_metal.mm SHADER 字符串):绑定 S(可变状态)、qn、kn、vh、alpha、beta、constant const int2 &dims。
两遍分解,完全不用 threadgroup 内存:由于 Pass 2 必须等 Pass 1 完成整组行衰减与 kS 累加,kernel 在单线程内串行化两个 pass——Pass 1 做行衰减S[i][j] *= alpha[i]并按列扫描累加kS[i] += kn[j]·S[j][i],随后计算vt[i] = (vh[i] − kS[i])·beta,Pass 2 做状态扩展S[i][j] += kn[i]·vt[j]并累加输出oh[j] += qn[i]·S[i][j]。依赖轴在线程内循环,独立轴跨 threadgroup 并行,因此不需要 threadgroup 内存或 barrier。
新增基础设施:backend_metal.h的coli_metal_kda_state()、backend_metal.mm 的k3_kda_statekernel +g_k3_kda_state管线 +P("k3_kda_state")编译 + dispatch 函数。状态绑定模型:S指向宿主分配的m->kstate[li]([H*hd*hd]),SharedBuffers 允许 GPU 零拷贝就地读写状态张量,无需 host-device 同步。CPU 路径(OMP 并行 + AVX2 向量化)在非 Metal 时保留作为正确性基准。
8.3 Conv1d + SiLU + L2 归一化(✅ DONE)
kda_conv_silu:每维度一个线程,把conv_win[d*K:K]窗口左移 1、新输入放入 K−1 位,与taps点积后过 SiLU(x/(1+exp(-x))),每投影(q/k/v)dispatch 一次共 3 次。kda_l2_norm:每头一个线程,对hd维求和q²/k²,计算1/sqrt(sum+eps)(eps=1e-6),Q 乘norm·qscale(qscale=1/sqrt(hd))、K 乘norm,单次 dispatch 覆盖所有头。新 API:coli_metal_kda_conv_silu()与coli_metal_kda_l2_norm()。
值得注意的缓冲对齐细节:卷积窗口状态缓冲(m->cwq/cwk/cwv[li])是calloc()分配(非页对齐),Metal 的wrap()使用MTLResourceStorageModeShared+parameters:nil自动注册任意宿主地址,无需falloc()。
8.4 融合 KDA token 步:一个 token 一个 MTLCommandBuffer(✅ DONE)
融合前每个 KDA token 跑 5 个独立 Metal command buffer(3×conv_silu + 1×l2_norm + 1×state),每个都要 create → encode → commit → waitUntilCompleted + readback,macOS 上每次往返约 150 μs,单 token 调度开销约 750 μs。coli_metal_kda_fused_token()把 5 个 kernel 顺序编码进单个MTLCommandBuffer,提交并等待一次,per-token Metal 开销从 ~750 μs 降到 ~150 μs,约 5 倍削减(非融合路径为 4 个 CB × 150 μs ≈ 600 μs,融合后约 150 μs,约 4 倍削减)。
CPU 预计算部分:alpha[H*hd](逐元素衰减,由graw、dt、A、gate_lb计算)和beta[H](每头门控,由braw计算)仍在 token 循环内的 CPU 上完成,代价 O(H×hd)=O(12288),相对 GPU dispatch 时间可忽略;meta_oh/meta_alpha/meta_beta每次 forward 调用预分配一次。缓冲布局兼容性设计很巧妙:conv_silu 的输出(qt/kt/tv=[H*hd])与 state kernel 期望的qn/kn/vh布局相同,L2 归一化就地改写qt/kt为qn/kn,同一 command buffer 内无需中间宿主缓冲。融合调用后 CPU 做每头 RMSNorm + sigmoid(full-rank gate) 产出on。
相关的类型修复:k3_matmul_f32()参数类型从ColiMetalTensor**改为void*,保证COLI_METAL未定义时也能编译。熔断机制:若融合 dispatch 失败,置g_k3_metal = 0并回退 CPU 路径(自愈式)。
功能测试(2026-08-01):prompt "hello"、20 token decode(C=1/token)、COLI_TEMP=0贪心采样——prefill(C=2,chunk=32)Metal 与 CPU 输出一致,decode 20 token 一致,diff metal_out.txt cpu_out.txt零差异。性能数据:单 token decode(K3_METAL=1、K3_CHUNK=1)有效 decode 3663 tok/s;Metal 感知 RSS 11.1 GB(融合路径)对比旧非融合路径 43.6 GB——零拷贝管理带来显著内存收益。
8.5 端到端全模型 A/B 验证(✅ DONE)
93/93 层全部在 Metal 上运行,Metal 路径通过g_k3_metal门控内联在kda_forward()与mla_forward()中,不存在独立的kda_forward_metal()/mla_forward_metal()函数。逐组件覆盖表(节选):KDA 投影走w_matmul()、低秩走k3_matmul_f32()、conv_silu/l2_norm 走 8.3、状态递归走融合 token 步、MLA KV cache 走coli_metal_kv_write()、MoE 专家 matmul 走coli_metal_moe_block()、lm_head 走w_matmul()、KV 清空走coli_metal_kv_clear();仍留在 CPU 的部分是 MLA 注意力循环、MoE 专家路由、RMSNorm/res_mix/moe_forward。
回退机制分三档:KDA 融合 dispatch 显式检查返回值(kimi_k3.c:824 附近),失败即自愈回退;MLAcoli_metal_kv_write仅在初始化失败(g_k3_metal==0)时走 CPU;其余所有 dispatch 由g_k3_metal && coli_metal_available()双重守卫。
A/B 测试(全部COLI_TEMP=0确定性贪心采样)五组全部 PASSED:短 decode("hello",20 token)、长 prefill(45+ token)、长 decode("The stock market crashed",50 token)、聊天模式("Explain quantum computing")、单 token prompt(--ids "151657")。聊天模式计时显示 Metal 与 CPU 几乎持平(prefill 91.4s vs 92.5s,decode 162.1s vs 162.8s,注意力 61.8s vs 62.9s)——文档明确分析瓶颈是 MoE 专家加载的磁盘 I/O(960.7 GB 流式读取、专家 cache 命中率仅 5.7%),而非 GPU dispatch;Metal 的收益要在专家 cache 命中率改善后才可测量。
逐层验证与全模型验证(Phase 9–10)
Phase 9 — 端到端逐层验证(✅ DONE)
新增K3_VALIDATE_LAYER=N/K3_VALIDATE_TOKEN=T基础设施,在指定层(token 0)导出 5 个中间张量到 ASCII 文件:nrm_attn(注意力块输入)、att(原始注意力输出)、nrm_mlp(MoE 输入)、mlp(MoE 原始输出)、hidden(层最终输出)。每张量 7168 个 float(D=7168)。3 层 × 5 张量 = 15 组对比全部maxAbs = 0.00e+00,Metal 与 CPU 逐位一致,证实融合 token 步、KV 写入、全量化级别 matmul(f32/q8/i4)、MoE 专家输出均无浮点重排效应。代码改动:g_k3_val_layer/g_k3_val_token/g_k3_val_fp全局量、val_dump()辅助函数、step_chunk()层循环捕获钩子(见 kimi_k3.c:1199)。
Phase 10 — 全模型验证与两个关键 bug(✅ DONE)
Bug 1:fmt=0 scales NULL 崩溃。症状是 Metal 推理在第 5 次 matmul(tanh 激活,fmt=0)时于wrap()内的newBufferWithBytes处 SIGSEGV(exit 139)——前 4 个 fmt=4 量化投影全部成功。根因:fmt_scale_bytes(fmt=0, ...)错误地返回O*sizeof(float)=512字节,导致wrap(scales=NULL, 512)走 tensor 创建路径,MetalnewBufferWithBytes:NULL在 GPU 读地址 0 时崩溃;调用方w_matmul()传scales=NULL本来是正确的。修复:fmt_scale_bytes()对fmt==0返回 0,并显式处理fmt==1(逐行 scalesO*4)、fmt==4(分组 scalesO*ceil(I/gs)*4)、fmt==6(2 字节组 scalesO*ceil(I/gs)*2)。文件改动:c/backend_metal.mm:588与c/kimi_k3.c:317。
Bug 2:晚初始化缓冲的f_free误释放。页对齐缓冲在k3_matmul_f32之后被w_addrow的_XLATE Case 8提前释放,使w_actual/w2_actual指针失效。修复:仅在缓冲确实为堆分配(跟踪分配标志)时才f_free,栈/内联缓冲跳过。
K3_X0=0 GPU 回退模式:GPU 只跑融合 KDA kernel(conv_silu、l2_norm、state),w_matmul回退 CPU matmul,用于把融合 kernel 正确性与投影正确性隔离验证。
执行结果:单 token(K3_METAL=1 K3_CHUNK=1)prefill 1/1、decode 3663 tok/s、exit 0 输出 "bba";CPU 参照输出一致;6 token 多 token 场景 exit 0。内存对比:Metal RSS 11.1 GB vs CPU 15.2 GB;I/O 流式 25.8 GB(单 token 主要由 MoE 专家磁盘加载主导)。
回归、性能基准与优化路线(Phase 11–13)
Phase 11 回归测试(进行中):矩阵 GLM(CPU ✓ / Metal ✓)、Kimi K3(CPU ✓ / Metal ✓)。已确认的事项包括:Metal 需要K3_METAL=1环境变量(非 CLI flag);调试输出经K3_DEBUG_OUTenv 写逐维 Metal/CPU 对比文件;模型维度来自config.json(kda_proj=12288、kda_heads=96、kda_hd=128、conv_k=4);Metal 初始化注册g_dev/g_queue、编译 SHADER、创建全部管线状态;状态递归oh输出在全部 128 维上一致到小数点后第 9 位,输入浮点(qn/kn/vh/alpha/beta)Metal 与 CPU 匹配到 6–7 位有效数字。尚待完成:GLM Metal 回归、多 token decode 回归(C>1 全量验证)、DSA Metal kernel、MLA 注意力循环 Metal 化、MoE 路由 Metal 化。文档还记录了"Meta KDA dot ratio 差异"这一活跃调查项:CPU_DOT_RATIO ~8.054e-3 vs META_DOT_RATIO ~5.432e-3(相差 1.48 倍),Metal 输出在 128 维上均匀、输入浮点已核对一致,根因待定位。
Metal 激活与调试命令(来自文档 Phase 11 小节,可直接复现):
# 构建(macOS 上,-DCOLI_METAL 由 make 传入) make -C c kimi_k3 METAL=1 # 运行(K3_METAL=1 触发 Metal 初始化) K3_METAL=1 ./kimi_k3 /Volumes/4Tb990Pro/Kimi-K3 "prompt" # 逐维 Metal/CPU 对比调试文件 K3_DEBUG_OUT=/tmp/k3_debug.txt # 限制生成 token 数做快速测试 K3_MAX_TOK=NPhase 12 性能基准:只在正确性确立后进行,指标包括首 token 延迟、decode tok/s、GPU 利用率、command buffer 数、kernel launch 数、host/device 传输量,输出到docs/kimi_metal_benchmarks.md。Phase 13 优化:只优化 profiling 认定的瓶颈,典型候选包括融合 RMSNorm+linear、专家批处理、减少缓冲拷贝、command buffer 批处理、持久 threadgroup、改进统一内存访问模式——每项优化合并前必须有可测量的基准改进。
推荐的 PR 结构与移植方法论
文档结尾给出适合简单自主编码 Agent 的小步 PR 结构:①差距分析与文档 → ②后端 dispatch 脚手架(无推理改动)→ ③稠密原语 Metal 实现 → ④MoE 专家执行 → ⑤KV cache 支持 → ⑥KDA 注意力 → ⑦全层验证 → ⑧Kimi K3 端到端 Metal 推理 → ⑨性能优化与基准。指导原则可以概括为三条可执行的铁律:
- 永远不发明新算法:CPU 是正确性基准、Vulkan 是 GPU 参考、既有 Metal 后端是实现模板;
- 每个 PR 只加一个 Metal 原语或一个测试,新原语必须通过 CPU-Vulkan-Metal 三方数值一致性校验(Phase 9 的 maxAbs = 0.00e+00 标准);
- 先正确后性能:数值等价确立前不做优化,优化必须有基准数据支撑。
这套方法论把"93 层大模型 GPU 移植"这个大工程拆解成每一步都可独立验证的小步骤,既降低了 Agent 单步需要理解的架构面,也保证了每一步的产出可复现、可回归——这正是 Colibri 将 2.8T 参数模型跑进 Apple Silicon 统一内存的关键工程实践。
【免费下载链接】colibriRun frontier MoE models on hardware you already own — pure C, zero deps, experts streamed from disk. Tiny engine, immense model. 🐦项目地址: https://gitcode.com/GitHub_Trending/colibri3/colibri
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考