在单台 Apple Silicon Mac 上运行 Kimi K3(2.8T 参数)的实验
Deltafin 是一个小型研究项目,它运行着一个远超所在机器规模的混合专家模型。目前,在我们 64 GB 的 M1 Max 上,精确路径中位数为 0.0687 token/秒(14.6 秒/token)。迄今为止,所有已发布的运行都来自那台第一代机器——而非更新的 Max 或 Ultra——并且,基于能力限制的路径加上自动内存预算管理,使得同一引擎能够延续到更新的 Apple Silicon Mac 上。
安装
三条命令,然后你就可以开始生成了。唯一需要真正做决定的是第三步。
# 1. environment (Python 3.12+, and Xcode CLT for clang)
python3 -m venv venv
./venv/bin/pip install torch numpy safetensors tiktoken ml_dtypes blobfile \
"transformers==4.56.2" einops tokenizers
# 2. build the fused MXFP4 kernel
clang -O3 -mcpu=native -shared -DNO_MAIN -o tools/libmxfp4gemv.dylib tools/fused_gemv.c
# 3. download the model (see the two modes below)
./venv/bin/python tools/setup_k3.py --full
两种模式
| --full(推荐) | --stream | |
|---|---|---|
| 所需磁盘空间 | 约 1.7 TB | 约 215 GB |
| 下载时间 | 5–10 小时,可断点续传 | 约 30 分钟 |
| 后续速度 | 在我们的 M1 Max 上,中位数为 14.6 秒/token | 对于任何尚未缓存的内容,约 3 分钟以上/token |
| 推理时的网络需求 | 无 | 持续连接 |
每个 token 需要读取 16 个专家 × 92 层 = 25.8 GB 的专家数据。从本地磁盘读取大约需要 4 秒;通过网络则需要几分钟。这一事实就是两列之间全部差异所在。
运行 `setup_k3.py` 时不加任何标志,当磁盘空间允许时它会选择 `--full` 模式,否则会回退到流式模式,并准确告知你需要释放多少空间。
从流式模式开始,后续升级
流式模式是尝试 Deltafin 的好方法,无需预先投入 1.7 TB 的磁盘空间。无论何时你想要更快的速度,一条命令即可完成升级——无需重新安装,无需重新配置,并且它会利用所有已缓存的内容:
./venv/bin/python tools/fetch_experts_all.py # resumable, run anytime
./venv/bin/python tools/fetch_experts_all.py --dry-run # just show the numbers
./venv/bin/python tools/fetch_experts_all.py --layers 1-40 # partial is fine too
对于流式安装,空闲时的预热器可以根据记录的路由器轨迹来识别缺失的专家。其默认是一个只读方案;网络获取是显式进行的,并且它可以原子地将旧的 `.npz` 条目转换为原始的快速格式:
./venv/bin/python tools/warm_expert_cache.py
./venv/bin/python tools/warm_expert_cache.py --convert-npz --fetch 128
每当 Deltafin 仍处于流式模式时,它会在启动时打印一条提醒——无论是对于 CLI 还是 API 服务器——显示当前本地已缓存的专家池比例以及完成全部缓存所需的代价。
可选:int8 脊柱网络
将非专家权重的每 token I/O 减半,在我们的检查中未发现明显的质量变化。只需几分钟:
./venv/bin/python tools/convert_spine_int8.py
使用方式
# ask a question; generates until the model finishes its answer
./venv/bin/python tools/kimi_run.py --chat --prompt "What are the three largest moons of Saturn?"
# raw completion (no chat template); runs until you press Ctrl-C, or cap it
./venv/bin/python tools/kimi_run.py --prompt "The capital of France is" --max-new 16
Token 会边生成边打印,因此你始终能看到文本实时呈现。按 Ctrl-C 可在任意位置干净地中断并输出已生成的结果;`--max-new N` 参数可限制生成长度。一个诚实的提醒:K3 在回答前会进行思考,大约每分钟生成 4.1 个 token,因此一次完整的聊天回答可能需要一段时间——观看其流式输出本身就是体验的一部分。
设置 `K3_TRACE=buffered` 可将路由选择记录到 `router_trace.jsonl` 文件中,供离线分析使用。性能测试运行时请关闭追踪功能。
兼容 OpenAI 的服务器
Deltafin 可提供标准的 OpenAI API 服务,因此聊天界面、openai SDK 以及编程智能体只需修改基础 URL 即可使用:
./venv/bin/python tools/serve_openai.py --port 8000
curl http://127.0.0.1:8000/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model": "deltafin-kimi-k3",
"messages": [{"role": "user", "content": "Hello!"}]}'
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="none")
r = client.chat.completions.create(
model="deltafin-kimi-k3",
messages=[{"role": "user", "content": "Hello!"}])
print(r.choices[0].message.content) # the answer
print(r.choices[0].message.reasoning_content) # K3's thinking, when present
已实现 `/v1/chat/completions`、`/v1/completions` 和 `/v1/models` 接口,流式传输(`"stream": true`)功能也可正常工作。大多数读取 `OPENAI_BASE_URL` 和 `OPENAI_API_KEY` 的工具,只需将地址指向该服务器即可运行。
在将任何自动化工具指向该服务器之前,请先阅读以下注意事项:
- 时间。回答会在准备好时返回——请将客户端的超时时间设置为小时级别,而非秒级。省略 `max_tokens` 参数可让模型完成整个回答(推荐);原始补全(raw completions)默认不会自行终止,其默认值为 256。运维人员可通过 `K3_SERVER_MAX_TOKENS` 设置硬性上限。
- 流式安装在此处的速度要慢得多。一个聊天模板提示词至少需要 60 个 token,且预填充阶段会触及每层的多个专家,因此在缓存部分填充的情况下,一次聊天请求可能需要数小时来获取结果。使用完整安装时,则只是正常的(慢速)推理。当处于流式模式时,服务器会在启动时打印一条警告信息。
- 仅支持贪婪解码。`temperature` 和 `top_p` 参数虽被接收但会被忽略,且一次只处理一个请求(第二个并发请求将收到 429 状态码)。
- 智能体目前仅作为探索性功能,并非正式工作流程。编程助手在理论上可以工作,但其冗长的系统提示词会导致预填充成本高昂。
配置
所有功能无需配置即可运行:Deltafin 会在有 GPU 时自动选择 GPU,在 int8 骨干网络已构建时自动选择该网络,并在启动时告知其选择结果。以下变量可用于覆盖默认设置:
| 变量 | 默认值 | 含义 |
|---|---|---|
| K3_DEV | auto | 可用时使用 GPU(mps),否则使用 CPU |
| K3_SPINE | auto | 已构建时使用 int8(推荐),否则使用 bf16 |
| K3_INT8_LM_HEAD | 1 | 在支持时使用打包的 MPS int8 输出头;精确的密集回退方案仍然可用 |
| K3_SPEC | 1 | n-gram 推测(无损) |
| K3_TEMPLATES | 1 | 模板层缓冲区复用 |
| K3_PRELOAD / K3_PREFETCH | 1 | 后台层加载 / 专家预取 |
| K3_METAL_POSITION_BATCH | 0 | 精确的主动启用 T>1 位置主序 Metal MoE;实测接受推测通过率提升 +2.0%,需按每台 Mac 重新调优 |
| K3_MOE_TOP_K | 16 | 明确的质量/速度调节旋钮;减少路由专家数可降低专家数据量并可能改变输出 |
| K3_CPU_MOE_BATCH | 自动 | 精确的持久化 CPU MXFP4 工作线程环;填充计数器在八线程下实测提升 +3.6% |
| K3_ASYNC_CACHE_WRITE | 0 | 主动启用的缓存未命中写入重叠;K3_CACHE_WRITE_QUEUE(4)限制未完成缓冲区数量,K3_CACHE_WRITE_WORKERS(1)可按每台 Mac 重新调优 |
| K3_APPROX | 0 | fp16 数值计算;在接近平局时不可复现 |
| K3_RAM_GB / K3_PIN_LAYERS | 自动 | 覆盖 RAM 预算 |
| K3_PROFILE | 0 | 每次推理的逐阶段计时 |
| K3_TRACE | 关闭 | 缓冲写入:每次推理一个路由器追踪块;立即同步写入每一层 |
| DELTAFIN_ROOT | 仓库根目录 | 缓存和权重存放的位置 |
| K3_HF_HOST / K3_HF_PATH | Hugging Face | 将专家获取指向镜像 |
| K3_SERVER_MAX_TOKENS | 无限制 | 可选的服务器生成硬上限 |
| K3_RESPONSE_MEMO_ENTRIES | 32 | 精确的进程内重放缓存,用于相同的确定性 API 请求;设为 0 则禁用 |
系统要求
- 一台 Apple Silicon Mac。所有已发布的数据均来自同一台初代 M1 Max(64 GB)——这是迄今为止唯一经过基准测试的 Mac。更多 RAM 会被自动利用(128 GB 的机器可固定数倍于当前模型的层数),而更新的芯片能带来更高的内存带宽、更多的 GPU 资源和更快的存储。详见《为什么更新的 Mac 应该更快》。
- Xcode 命令行工具,用于 clang(xcode-select --install)。
- Python 3.12 或更新版本。
- 磁盘空间:完整安装约需 1.7 TB,流式模式约需 215 GB(详见安装说明)。
- 可访问 Hugging Face 的网络连接。
工作原理
K3 的权重总计约 1.56 TB,超过了这台机器的可用磁盘空间,更不用说 RAM 了。然而,本地推理之所以仍然可行,关键在于混合专家模型每个 token 只触及自身的一小部分。
- 常驻主干(约 114 GB:注意力层、共享专家、潜在投影、嵌入向量)一次性下载完成,每个 token 从本地 NVMe 逐层读取,量化为 int8 后在 GPU 上计算。
- 82,432 个路由专家(约 1.45 TB)。对于每个 token,K3 的路由器为每层选取 16 个专家,并且只读取这些专家。如果条件允许,建议在本地全部安装;否则,Deltafin 会按需从 Hugging Face 获取它们——每个专家一个 HTTP 范围请求——存入不断增长的磁盘缓存中。
- 前向传播运行的是 Moonshot 自身的建模代码,未经修改。一个纯 PyTorch 的小型垫片替代了其所需的仅支持 CUDA 的 fla 内核。
flowchart LR
subgraph HF["Hugging Face CDN"]
W[("96 safetensors shards<br/>1.56 TB · MXFP4")]
end
subgraph MAC["MacBook (M1 Max, 64 GB)"]
subgraph DISK["NVMe"]
SP[("resident spine<br/>114 GB bf16 → 60 GB int8")]
EC[("expert cache<br/>raw shard spans")]
end
subgraph TOK["per token"]
R{"router<br/>top-16 of 896<br/>× 92 layers"}
L["93 decoder layers<br/>2 shared GPU templates"]
K["fused MXFP4 GEMV<br/>NEON"]
end
end
W -- "one range request<br/>per missing expert" --> EC
SP -- "double-buffered<br/>layer loader" --> L
EC -- "mmap" --> K
R -- "selected experts" --> K
K --> L
L -- "logits" --> R
预期表现
以下所有当前数据均在一台 M1 Max(10 核 CPU、32 核 GPU、64 GB 内存、内置 NVMe)上测得,模型完整安装在本地,采用 int8 驻留权重和输出头、Metal MoE、精确 fp32 数值计算、贪婪解码,并禁用了追踪功能。
当前列汇集了来自平衡的 ABBA/BAAB 测试序列的六次精确全模型运行结果。每次运行使用五个 token 的提示词 The capital of France is,验证了三个 token 的补全结果 Paris. The,并在报告稳定吞吐量之前丢弃了第一个解码步骤。数值为中位数;范围显示了即使在同一台空闲机器上,这个 I/O 密集型工作负载的波动幅度。
| 指标 | 首个可用版本 | 当前 M1 Max 基准测试 | 变化 |
|---|---|---|---|
| 预填充 / 首个 token(5 token 提示词) | 2,429 秒 | 28.0 秒中位数(24.9–37.9 秒) | 约 87 倍 |
| 稳定解码,专家本地化 | 约 20 分钟/token | 0.0687 token/秒(14.6 秒/token);运行范围 0.0503–0.0779 token/秒 | 约 82 倍 |
| 精确的三 token 生成,模型时间 | — | 56.5 秒中位数 | — |
| 同次运行的新进程墙钟时间 | — | 64.1 秒中位数 | — |
| 解码,专家流式传输 | 约 20 分钟/token | 约 3 分钟/token | 受网络限制 |
这就是“不起眼的 M1”的结果:一台老化的第一代 M1 Max,而非更新的 Max 或 Ultra。这是一个保守的参考点,并非跨 Mac 的基准测试。我们预计更新、带宽更高、内存更大的系统表现会更好,但在有人测量出这些数据之前,我们会单独标注那些数值。
近期精确路径的改进
以下最新的测量结果是在同一台 M1 Max 上进行的平衡 A/B 测试。每次全模型运行都检查了 token 预言机。
| 变化 | 测量结果 | 发布时的行为 |
|---|---|---|
| 打包的 MPS int8 输出头 | 稳定解码中位数提升 +17.3%,预填充提升 +23.1%,墙钟吞吐量提升 +26.8%;驻留输出头存储从 4.7 GB 降至 1.17 GB | 在算子与 int8 权重均可用时启用,并带有异常保护的密集回退机制。 |
| 仅作参考的推测性快照。 | 0.001 毫秒而非 3.56 毫秒,且无需约 475 MB 的状态克隆。 | 默认启用;重放与部分接受测试可保留精确的未来序列。 |
| 面向已接受草稿的按位置主序 Metal MoE。 | 在真实 T=2 层上提升 +4.7%,全模型吞吐量汇总提升 +2.0%。 | 通过 `K3_METAL_POSITION_BATCH=1` 精确选择加入,待按每台 Mac 进行调优。 |
| 128 字节对齐的 CPU 工作线程计数器。 | 四线程时提升 +0.2%,八线程时提升 +3.6%。 | 在持久化 CPU 回退中自动生效。 |
中位数约为每分钟 4.1 个 token。一个典型的 M1 Max 性能画像主要由以下部分组成:
| 等待驻留主干读取(53 GB) | 约 5 秒 |
| 读取每层选定的 16 个专家(25.8 GB) | 约 4.3 秒 |
| 应用主干(传输 + 反量化) | 约 3 秒 |
| 注意力机制与归一化(93 层) | 约 2 秒 |
| MoE 专家矩阵乘法 | 约 1 秒 |
解码阶段现在受限于驻留主干的磁盘带宽。这 53 GB 数据在每个 token 生成时都会被重新读取,而在此访问模式所能维持的约 7 GB/s 速率下,这大约占用了 14.6 秒中位数中的 7.5 秒——除非拥有更多 RAM(足以容纳主干而不挤占专家读取所需的页缓存),或者采用更小的主干,否则无法避免。
为何新款 Mac 应该更快
上述每一行都受限于 Apple Silicon 各代芯片间变化的硬件。M1 的结果并未完全阻断任何路径,但这里测量到了若干回退值;新款机器应根据自身的能力、运行时环境、内存和存储特征重新调优这些参数:
- 内存带宽。M1 Max 为 400 GB/s。M3/M4 Max 显著更高,而 Ultra 型号则大致翻倍——这直接影响主干加载和专家矩阵乘法。
- GPU。更多核心能以更快速度执行相同的 Metal 内核;反量化着色器和注意力路径均随核心数扩展。
- SSD。专家读取是占比最大的单一环节,其速度取决于内置硬盘的性能。后续 Mac 机型配备了更快的 NVMe。
- 内存。这是最重要的因素。53GB 的主干模型无法与其它所有内容一起放入一台 64GB 的机器中,因此每生成一个 token 都需要从磁盘重新读取——这大约占总耗时的一半。在 128GB 的机器上,它可以直接留在页面缓存中,这部分开销基本消失。Deltafin 也会自动将更多模型固定在那里,无需任何标志位。
运行时选择基于 Metal 特性族和算子能力检查,而非芯片名称字符串。这使得更新的系列(包括 M5)能够执行 M1 无法运行的路径,同时每个可选的本地路径都保留了精确的回退方案。
我们仅在这台 M1 Max 上进行了基准测试。如果你在 M3、M4 或 M5、Ultra 芯片,或者 128GB 及以上内存的机器上尝试,我们非常希望看到你的数据——请提交一个 issue,附上 `K3_PROFILE=1` 的输出和你的芯片型号。
当 n-gram 推测解码接受一个草稿时,一次前向传播会输出两个 token,因此重复文本的运行速度会成比例地加快。推测解码是无损的:被接受的草稿会精确复现参考序列,而被拒绝的草稿则会逐比特恢复模型状态。
两行解码结果之间的差距,正是完整安装(见上文)的全部意义所在:当专家模型在本地时,每个提示词都以第一行的速度运行,而不仅仅是那些专家恰好被缓存的提示词。
输出是贪婪且可复现的:相同的提示词每次运行都会产生相同的 token。
法国的首都是 → 巴黎。埃菲尔铁塔位于巴黎。卢浮宫博物馆也在巴黎。卢浮宫有……
需要明确其局限性:这是一个研究原型,而非实用的聊天配置。14.6 秒的中位数 token 生成速度距离交互式体验还很遥远,且长提示词成本高昂,因为预填充阶段会触及许多专家模型。我们认为它主要作为存在性证明和流式推理技术的测试平台而有趣。
技术方法
以下每种技术在保留之前,都已在真实权重上进行了测量。这些技术本身大多并非新颖;其中大部分是将下文致谢项目中提出的思路适配到 K3 的特定形态上。
输入/输出与流式处理
- 合并专家数据获取。每个专家的六个张量在分片文件中恰好是连续的(我们检查了全部 82,432 个分片),因此整个专家只需通过少量保持连接的连接池发起一次 17.55 MB 的范围请求。实测速度比逐个获取张量快约 6.4 倍。
- 原始字节磁盘缓存。缓存文件直接存储分片的原始字节——无容器格式,无解析过程。
- 并行专家读取。一个层中的 16 个选定专家由线程池使用带 `F_NOCACHE` 标志的 `pread` 系统调用同时读取,而非在内核计算时逐页按需缺页中断。实测冷启动数据:缺页中断方式为 0.87 GB/s,而读取方式为 6.85 GB/s。这使读取路径上每个 token 的处理时间从 40 秒降至 4.3 秒,并且 `F_NOCACHE` 能防止每 token 25 GB 的专家数据流量驱逐主干网络所需的页缓存。
- 双层缓冲加载。当前层计算时,工作线程同时读取下一层的主干数据。
- 前序 token 预取。在去重后的保留测试集上,连续 token 约有 31% 的专家选择会重复,因此每个 token 的专家集合会在后台为下一个 token 预先获取。
计算
- 融合 MXFP4 反量化与 GEMV 运算(`tools/fused_gemv.c`)——一个 NEON 内核,通过 16 条目查表一次性完成反量化与乘法,其中 e8m0 缩放因子以整数运算形式应用于 fp32 指数。该实现与参考实现逐位匹配,取代了原先慢得多的“先反量化再矩阵乘”路径。Metal 版本已作为验证原型存在。
- 模板层缓冲区复用。全部 69 个 KDA 层共享一组张量形状,全部 24 个 MLA 层共享另一组,因此两个持久驻留 GPU 的“模板”层可通过 `copy_()` 操作接收各层的权重。这避免了性能分析显示占每 token 处理时间很大比例的内存分配器开销。
- int8 驻留主干网络。将每 token 的驻留 I/O 量减半。在我们的检查中,前 5 个候选下一 token 的顺序保持不变,最高 logit 值仅变动 0.07%。
- 自定义 Metal 反量化内核。加载主干层的大部分时间都花在行广播乘法上,MPS 对此操作的运行速度为 43 GB/s,而相同字节的纯拷贝速度可达 334 GB/s。一个融合了 int8→fp32 转换、行缩放和拷贝操作的简短 compile_shader 内核,速度达到了 297 GB/s;通过使用持久化暂存缓冲区并将传输操作从调度之间提升出来,每层加载时间从 118 毫秒降至 21 毫秒。逐位精确:每个张量上的 max|diff| = 0。
- 打包的 int8 输出头。内置的 MPS 仅权重量化矩阵乘法直接使用现有的行 int8 检查点,避免了 4.7 GB 的 fp32 头及其反量化操作。能力检查和一个捕获到的密集回退机制,确保了在多个 PyTorch 版本和 Apple GPU 系列上的兼容性。
- 纯 PyTorch KDA 适配层(tools/fla/)—— Kimi Delta Attention 的循环、短卷积和门控归一化,从 fla-core 的语义移植而来。分块执行和逐步执行的结果差异约为 1e-9。在解码阶段,循环在 CPU 上运行,其小状态比在 GPU 上执行一系列调度更适合放在 CPU 上。
解码
- N-gram 推测。草稿通过对已生成文本进行后缀匹配而免费获得,并在一个共享固定成本的双位置批次中进行验证。这在此处是有价值的,正是因为常驻 I/O 和计算(而非专家提取)主导了热 token 的处理。在我们的测试中,接受的草稿精确复现了参考序列。回滚操作保留旧的不可变状态对象,而不是克隆约 475 MB 的数据,然后在常数时间内恢复它们;重放测试则保证了未来序列的精确一致性。
sequenceDiagram
participant D as n-gram draft
participant M as model (one T=2 pass)
participant S as state snapshot
D->>M: [last_token, draft]
M->>M: 93 layers, shared cost
alt draft verified
M-->>D: 2 tokens accepted
else draft wrong
S-->>M: state restored (bit-exact)
M-->>D: 1 token, nothing lost
end
随 RAM 扩展
- 在启动时,Deltafin 为操作系统预留内存(max(10 GB, 18%)),并将尽可能多的常驻层固定在剩余内存允许的范围内。一台 128 GB 的机器无需任何配置就能固定比 64 GB 机器多几倍的层,并且专家缓存还能额外受益于任何空闲的页面缓存。
未来方向
大致按优先级排序:Metal 专家内核(已原型化)、一个合适的质量评估框架(针对官方 API 的平均负对数似然),以便可以测量而非争论有损的速度/质量权衡、更智能的专家预取,以及最终一个遵循 ds4 精神的原生引擎,届时大部分剩余开销应该会消失。
致谢
Deltafin 大量借鉴了他人公开发布的研究成果。按影响力大致排序如下:
- colibri(JustVugg,Apache-2.0 许可)—— 展示了 744B 参数的 MoE 模型可在 25 GB 内存中运行,我们从中学习了路由器预取、专家固定技术、macOS 上的 F_NOCACHE 和 F_RDADVISE 策略,以及逐分片转换模式。其 M5 Max 性能报告 —— CPU 自旋等待会抢占共享功耗预算导致 GPU 饥饿 —— 改变了我们的任务调度方式。
- ds4 / DwarfStar(Salvatore Sanfilippo,MIT 许可)—— 我们研究过的最清晰的专家流式传输设计:零拷贝专家缓冲区、掩码分发、基于选择的缓存淘汰、会话持久化,以及我们直接采用的质量评估方法(与官方 API 输出的平均负对数似然对比)。其核心理念 —— 正确性优先于速度,用计算掩盖 I/O —— 非常合理,我们努力遵循了这一原则。
- 月之暗面(Moonshot AI)—— 感谢他们公开了 K3 的权重并附带了可读的建模代码(Deltafin 直接运行该代码),以及 Kimi Delta Attention 设计,其小型循环状态使得在笔记本电脑上实现长上下文成为可能。
- flash-linear-attention(fla-org,MIT 许可)—— 我们的 KDA 适配层移植了其内核和参考实现的语义。
- llama.cpp / ggml —— 内核内反量化与 MXFP4 处理的先前技术,也是本地推理社区大部分知识的基础。
- PyTorch、Transformers、ml_dtypes(我们用于 e2m1 的位精确性参考)以及 tiktoken。
许可协议
Deltafin 自身的代码采用 MIT 许可。本仓库中有两项内容不属于我们:
- tools/fla/ 是 flash-linear-attention(MIT 许可,© 2023–2026 Songlin Yang, Yu Zhang, Zhiyuan Li)语义的纯 PyTorch 移植。该署名已在文件头部和 LICENSE 文件中重复声明。
- Kimi K3 的权重和建模代码归月之暗面所有,并依据月之暗面自身的许可协议分发。这些文件在设置时下载,从未在此处进行供应商化 —— 使用前请阅读该许可协议。
Deltafin 是一个独立项目,与月之暗面无任何关联。