一台普通游戏电脑,跑起 1250 亿参数的模型
2026 年 9 月 24 日,ID 为 Niko1221 的开发者把一份叫 Strata 的推理引擎推上 GitHub。他在 Reddit 的 r/LocalLLaMA 版里写的开场很直白:之前他用 llama.cpp 在 12 GB 显存的 RTX 5070 上跑 Qwen3.8-Flash-Next 的 IQ3_XXS 量化,输出只有 15 token/s,提示处理 100 到 120 token/s;他不想换显卡,就为这一个模型、这一类电脑重写了一套引擎,同一份量化文件现在跑到 65 token/s,提示处理约 430 token/s。
六天之后,这个仓库有了 1,901 个 star、215 次 fork、99 个 issue 和 93 个 pull request,进了当天的 GitHub 日榜。它只服务一个模型——不是通用框架,不是给所有人用的东西。
本文的数字来自四处:仓库源码、README 与 DETAILS 文档、那份 9 页的论文、bench/results/ 下的 12 组原始基准记录,以及 99 个 issue 里的公开讨论。文末台账把每个数字的来源和口径逐条列出。
文中有边框的块是社区 issue 里的实测数据与用户原话,只作陈列,不加评价;正文的判断只建立在仓库文件、论文与作者文档上。
结论先说:「12 GB 显存跑 1250 亿参数模型」的准确含义,是显存里装下每个 token 必读的那 3.5 GB 稠密权重,再塞进几千个最常用的专家;内存装下全部 24,576 个专家,由 CPU 就地补算没上卡的部分;硬盘放一张每次只翻几行的查表。三层之间靠 PCIe 搬运。
Qwen3.8-Flash-Next 是一个 1250 亿参数的混合专家(MoE)模型,但每个 token 只激活其中 60 亿参数。它的 48 层里,36 层用的是 Gated DeltaNet——一种递归混合器,状态定长,不像标准注意力那样随上下文增长;另外 12 层用 Qwen 的稀疏注意力,只有两个键值头,无论上下文多长,最多只关注约 2,048 个被选中的位置。每一层末尾是一个 512 个专家的混合层,路由器每次挑 10 个,也就是说一个 token 只碰得到 2% 的专家权重。
这组结构决定了模型的字节预算。论文第一张表把它拆成四块:以 2-bit 的 Q2_0 文件为例,全部 66 GB 里,48 层乘 512 个路由专家占 34.0 GB,而每生成一个 token 只需要读其中 0.66 GB,正好是 2%;混合器、注意力、共享专家和路由器加起来 1.8 GB,门控残差 1.3 GB,输出头 0.44 GB,这三部分每个 token 都要完整读一遍,合计约 3.5 GB,稳稳放得进 12 GB 显存;一张 288 亿参数规模的 n-gram 嵌入表占 28.8 GB,但它是用最后三个 token id 寻址的,每次只读几行,加起来不超过 23 KB,放固态上按需读就够;最后是模型自带的单层多 token 预测(MTP)层 0.8 GB,用来做草稿。
关键在「每 token 只读 2%」这一条。如果 MoE 的路由是均匀的,那么无论怎么分层,每个 token 都得把 34 GB 专家过一遍,12 GB 显存救不了。正因为路由严重不均,才存在「把热的那一小撮钉在显存里」这个空间——这是整个引擎成立的唯一前提,也是第四章那个自适应专家缓存要解决的问题。
口径说明:下面所有「文件数 / 行数」均为本地克隆后清点。C++/CUDA 指 .cpp .hpp .cu .h 四种后缀,不含 third_party/ 里那份 vendored 的 ggml 绑定;换一个口径数字就变,所以引用时必须带上范围。
工作树 15.5 MB(克隆含 .git 是 27.4 MB)。按上述口径,仓库里有 223 个 C++/CUDA 文件、56,357 行;另有 35 个 Python 文件、11,646 行;22 个 Markdown 文件、1,985 行。分目录:
| 目录 | 文件数 | 行数 | 体积 | 内容 |
|---|---|---|---|---|
src/ | 123 | 45,692 | 2.7 MB | 引擎本体,C++/CUDA |
include/ | 88 | 8,365 | 651 KB | 与 src/ 一一对应的公开头文件 |
tests/ | 10 | 1,699 | 116 KB | HIP 路径与文件读取的对照测试 |
tools/ | 2 | 601 | 288 KB | 两个 C++ 工具,另有 19 个 Python 脚本、4,023 行 |
serve/ | 6 | 3,381 | 473 KB | 服务端,纯 Python 加一个浏览器前端 |
docs/ | — | — | 11 MB | 文档、论文、插图(正文 1,423 行) |
bench/ | — | — | 536 KB | 12 组带日期的基准记录,73 个文件 |
data/ | — | — | 1.2 MB | 词表、专家画像、实验性控制向量 |
third_party/ | 1 | 1,911 | 141 KB | vendored 的 ggml 绑定(上面的数都不含它) |
目录划分不是按文件类型,而是按调用关系:
Strata/
├─ START-HERE.bat 双击入口(Windows),2,406 字节
├─ SETUP.bat 改设置 / 装第二个模型
├─ setup.py 安装器本体,116 KB
├─ setup.sh Linux 上的同一套问答
├─ CMakeLists.txt 编译规则,48 KB
├─ chat.py 终端对话
├─ src/ 引擎本体
│ ├─ program/ 主生成循环 generate.cpp,5,116 行
│ ├─ core/ 专家缓存 · 分层 · 会话 · 验证 · MTP 草稿 · 权重装载
│ ├─ kernels/cuda/ 44 个 .cu:注意力、路由、MoE、量化解码、采样器
│ ├─ kernels/cpu/ 11 个文件:同一批算子的 AVX2 / AVX-512 版本
│ ├─ prefill/ 分块预填、MMQ 量化矩阵乘、PCIe 流水线
│ ├─ spec/ 推测解码控制器与草稿策略
│ ├─ ngram/ 读那张 28.8 GB 的查表
│ ├─ artifact/ GGUF 解析与反量化
│ ├─ plan/ 决定专家摆在显卡 / 内存 / 硬盘
│ └─ platform/ 直读文件与内存分配
├─ include/strata/ 公开头文件,与 src/ 一一对应
├─ serve/ 服务端(Python)
│ ├─ server.py 1,784 行,OpenAI / Anthropic 双协议
│ ├─ mcp.py 669 行,MCP 工具桥
│ ├─ frontend.py 488 行,浏览器应用的服务端
│ ├─ telemetry.py 267 行,Monitor 页签的指标
│ └─ web/ 浏览器前端:app.js 968 行
├─ tools/ 打包、词表、专家画像、HIP 调优(19 个 .py)
├─ bench/results/ 12 组带日期的基准记录,每组一个 README
├─ docs/ README · DETAILS · 多卡 · AMD · 论文 PDF
├─ data/ 词表 · 专家画像 · 实验性控制向量
└─ tests/ HIP 与文件读取的对照测试
最外面三个文件值得单独点出来。START-HERE.bat 只有 2,406 字节,本身不做实事,只负责找 Python、没有就装一个到用户目录、建一个隔离的虚拟环境,然后把剩下的活交给 setup.py。这个安装器有 116 KB,是整个仓库最大的单个文件之一:探测显卡、判断该用哪个预编译引擎、该不该现场编译、该下哪些模型分片、要不要开图像支持、上下文给多大,最后写出一份运行配置。CMakeLists.txt 有 48 KB——它按 GPU 的计算能力选择内核实现,AMD 的路径走 cmake/hip_backend.cmake。
引擎本体在 src/,123 个文件、45,692 行,占全仓代码的八成。最大的一份是 program/generate.cpp,5,116 行,主生成循环:读提示、解码、校验草稿、吐字。core/ 是 18 个文件、8,330 行,装着专家缓存、层、会话、验证、MTP 草稿、权重装载、布局、进度这些彼此独立的模块。kernels/ 最大,82 个文件、26,679 行,其中 kernels/cuda/ 44 个 .cu、kernels/cpu/ 11 个文件——同一批算子在 CPU 上的 AVX2 与 AVX-512 版本。这个「CUDA 一份、CPU 一份」的对称结构,就是「显卡算一半、CPU 算另一半」那套设计在代码里的形态。prefill/ 管分块预填、量化矩阵乘(MMQ)和 PCIe 流水线。
serve/ 是 Python 那一半,6 个源文件、3,381 行(另有三个 test_*.py 共 1,387 行)。server.py 1,784 行,同时实现 OpenAI 的 /v1/chat/completions 和 Anthropic 的 /v1/messages 两套协议,含流式输出、工具调用、思考档位和会话检查点。mcp.py 669 行,把 MCP 服务器的工具递给模型调用。telemetry.py 267 行,提供 Monitor 页签上那些实时指标。web/app.js 968 行,是浏览器里那个带三个页签的应用。
还有四项不像产品代码、但决定了这个项目可核查程度的东西。
基准记录。 bench/results/ 下有 12 组带日期的目录,从 2026-09-24-final 一直到 2026-09-29-speed-0126,共 73 个文件。每组都留着原始 JSON(matrix.json)和一份写清楚「这次改了什么、数字从多少到多少、哪一步是逐位等价的」的 README。项目对外报出的每一个性能数字都能在这里找到原始出处。第六章逐组过一遍。
文档。 docs/ 的 11 MB 体积几乎全在示例图上,正文 1,423 行:README.md(221 行,使用者视角,从「怎么装」讲到「出错了怎么办」)、DETAILS.md(603 行,全部实测表、API 端点、27 行故障排查表)、9 页的 Strata-Paper.pdf、讲多卡层切分的 MULTI_GPU.md(122 行)、讲 AMD 后端的 AMD_HIP.md 与 AMD_HIP_PERFORMANCE.md、讲第二个 GPU 当专家缓存的 SECOND_GPU.md(84 行,明确写着这是实验)、给 OrcaRouter 未审查版模型的 ORCA.md。另有一份 docs/media/make_figures.py——README 里那两张说明插图是用代码画的,脚本也在仓库里。
工具链。 tools/ 下 19 个 Python 脚本(4,023 行),是这套引擎的前半场:strata_pack.py(423 行)把 GGUF 重打包成引擎要的格式,iq_pack.py(345 行)处理 i-quant 与兼容性转换(OrcaRouter 那条路要走它的 --compat-bf16),canonical_xcheck.py(799 行)做打包正确性交叉校验,make_profile.py 与 calibrate.py 负责专家画像和本机调优,draft_vocab.py(94 行)构建推测解码的草稿词表,needle_bench.py 跑长文档针尖测试。三个 test_*.py 给这些工具自己写测试。
数据。 data/ 里是四样算出来的结果,不是代码:expert-profile.bin(哪些专家优先放进显存)和 expert-profile-coder.bin(Coder 版对应的一份,映射到保留的那一半专家,12 GB 卡上约 72% 的专家读取命中显卡)、draft_vocab.bin(草稿词表,第八章那个中文问题就出在这个文件上)、experimental-speed-projection/ 里一个 483,520 字节的控制向量(第九章单独说)。
关于与 llama.cpp 的边界,作者在致谢里列得很清楚:借来的是 i-quant 量化格式、转写在 src/kernels/cuda/iq_kernels.cu 里的 GPU 点积与反量化内核、CPU 端链接的 ggml 后端、图像编码用的 mtmd 库、工具链里的 gguf-py;调度、专家分层、推测解码、整条服务链是自己写的。量化格式这一层选择兼容,运行时编排这一层选择重写——格式兼容意味着现成的模型文件直接可用,编排重写意味着它不必背负通用引擎的历史包袱。
引擎的许可划分也照着这条边界走:Strata 本体是 MIT;third_party/ggml 是 llama.cpp 的 MIT;网页应用的字体是 Outfit(SIL Open Font License 1.1);那个实验性控制向量是 Qwen Community License 1.0,因为它是从模型激活值里做出来的。模型权重不在仓库里,各自适用各自的许可。
这一章按装机顺序讲:每一样部件在引擎里承担什么、它的天花板在哪、加钱加在哪一样上收益最大。
先给最低门槛,再说怎么往上加。
| 部件 | 要求 | 在引擎里承担的职责 |
|---|---|---|
| 显卡 | NVIDIA RTX 20 / 30 / 40 / 50 系,显存 ≥ 12 GB | 每 token 必读的稠密权重、注意力、被缓存的专家 |
| 内存 | 64 GB 建议(48 GB 只够 Q2_0 / IQ2_XS) | 装下全部 24,576 个专家(钉住),CPU 就地计算 |
| CPU | x86-64 + AVX2 | 计算没上显卡的专家;AVX-512 有一版更快的内核 |
| 磁盘 | 空余 70–80 GB,NVMe 强烈建议 | 28.8 GB 查表 + 模型分片;低内存模式下还要存专家 |
| 系统 | Windows 10 / 11 或 Linux | Ubuntu 22.04 / 24.04 的依赖会自动装 |
| 驱动 | NVIDIA 580 或更新 | 唯一需要自己装的东西 |
显卡:显存容量比显存速度重要。 这是论文里最反直觉、也最好用的一条判断。每多 1 GB 显存就能多装约 700 个专家,而每一个装在显存里的专家,就是 CPU 不用算的一个。显卡自己的活(每个校验窗口约 14 毫秒,主要是读那 3.5 GB 稠密权重)是第二重限制,那部分才由显存带宽决定。所以在同一代里,24 GB 的卡即使核心更慢,实际输出速度也可能明显高于 16 GB 的卡。
论文里 13 条发现中有一条正是这个:把显存槽位按字节算而不是按个数算——IQ3_XXS 的专家大小在 1.3 到 2.3 MB 之间浮动,按层给槽位而不是统一按最大的专家给,能多装 883 个专家(2,673 变 3,556),1K 上下文下的解码快 13%。
DETAILS 里给了一张按此推算的估算表(标注 ±20%,单位是「提示 / 输出」token/s,提示部分的估算早于 0.1.13,那次改动把长提示提速了约一倍,所以提示列偏保守):
| 显卡 | 尺寸 | 1K 提示 / 输出 | 4K 提示 / 输出 | 32K 提示 / 输出 | 128K 提示 / 输出 | 262K 提示 / 输出 |
|---|---|---|---|---|---|---|
| RTX 5060 Ti 16GB | Q2_0 | ~341 / ~80 | ~472 / ~87 | ~501 / ~81 | ~476 / ~62 | ~435 / ~53 |
| RTX 5060 Ti 16GB | IQ2_XS | ~291 / ~80 | ~406 / ~77 | ~434 / ~63 | ~413 / ~51 | ~383 / ~47 |
| RTX 5060 Ti 16GB | IQ3_XXS | ~249 / ~66 | ~359 / ~65 | ~381 / ~56 | ~363 / ~45 | — |
| RTX 3090 24GB | Q2_0 | ~355 / ~128 | ~491 / ~140 | ~521 / ~130 | ~495 / ~100 | ~453 / ~85 |
| RTX 3090 24GB | IQ2_XS | ~303 / ~131 | ~422 / ~128 | ~451 / ~103 | ~430 / ~85 | ~398 / ~78 |
| RTX 3090 24GB | IQ3_XXS | ~260 / ~106 | ~374 / ~103 | ~396 / ~89 | ~378 / ~71 | — |
16 GB 到 24 GB,提示速度几乎没变(4K 下 ~472 → ~491),输出速度从 87 涨到 140——+61%。这 8 GB 显存换来的就是这个全部。文档里那句话说得直接:更大的显存比更快的核心重要;每多 1 GB 就能多装约 700 个专家,而每个在显卡上的专家,都是 CPU 不用算的一个。
内存:容量决定能不能跑,带宽决定跑多快。 专家区本身就占 34 GB(Q2_0)、35.5 GB(IQ2_XS)或 43 GB(IQ3_XXS),加上系统和你要开的程序,64 GB 是「刚好够」——用 IQ3_XXS 时要少开别的程序。README 给的公式是:内存 ≥ 第一分片 + 约 10 GB。
带宽同样关键:CPU 那一半的速度受限于内存供给,实测并发时约 41 GB/s、单独跑约 52 GB/s(DDR5-5200),而 Q2_0 的 CPU 专家内核已经在以约 42 GB/s 读权重,基本吃满。作者的建议很直接:在 BIOS 里打开 EXPO / XMP,让内存跑在标称频率上。DDR4 平台大约只有 DDR5 一半带宽,Q2_0 的 CPU 半个回合会花掉约两倍时间;i-quant 受算力限制而不是带宽限制,损失小一些。无论哪种情况,更大的显卡缓存都是最好的补偿。
CPU:AVX2 是硬门槛,AVX-512 是实打实的增益。 论文发现第 6 条:把 Q2_0 专家内核针对 512 位 AVX-512 重写(每 64 个权重一次解包、一次 VNNI 点积)之后,CPU 侧达到约 42 GB/s,接近内存能供上的上限。i-quant 的情况不同(发现第 7 条):解一个码本权重要查表、要处理符号掩码,ggml 的 AVX2 内核和这套 AVX-512 内核都只跑到每核约 5 GB/s、六核 23 到 26 GB/s——i-quant 卡在算力上,不在带宽上,这就解释了为什么 IQ3_XXS 只比 Q2_0 大 25%,实际却慢得多。
另外 CPU 支持 AVX-512 时,Q2_0 会额外写一份约 40 GB 的专家副本给 CPU 内核用(一次性),装机时磁盘要留出来。
磁盘:NVMe,并且留够。 28.8 GB 的 n-gram 查表放这里,每次只读几行;模型分片 66 到 76 GB;图像支持再加约 1 GB 的编码器;AVX-512 机器上再加那 40 GB 副本。0.1.26 起的低内存模式(--mmap-experts)会用磁盘换内存:把全部专家从模型目录里的一个文件(experts.bin)映射进来,由操作系统的页缓存兜住显卡没拿的部分,但它会把内存还给系统;需要额外 23 到 50 GB 磁盘。在 Coder 上,引擎的提交内存从 36 GB 降到约 13 GB,答案不变。代价是分两头:大显卡(如 RTX 5090 装得下 Coder 全部专家)几乎照常速度;小显卡上大部分专家要从固态读,慢得多,安装器会明说。
PCIe:不是带宽瓶颈,是调度资源。 PCIe 4.0 x16 约 26 GB/s,而提示路径只用到约 7 GB/s,远没跑满。真正起作用的是显卡的拷贝引擎:让一部分未命中显存的专家由 DMA 直接从内存搬给显卡,搬运算不算得完不占用任何一方。这个比例是分尺寸调的(发现第 9 条)——i-quant 的 CPU 受算力限制、内存带宽空着,所以给它 55% 的未命中走 PCIe,IQ2_XS 在 4K 下从 70 涨到 78、IQ3_XXS 从 56 涨到 65;Q2_0 只给 20%,因为它的 CPU 已经在吃满带宽,DMA 只会来抢。安装器会根据实测给这个参数(--pcie-frac)。
多卡:两张合适,第三张要慎。 两张或三张卡可以跑同一个模型,走层切分:48 层切成连续区间,第一张跑前 K 层,第二张跑 K 之后,最后一张兼管输出头和草稿层。每张卡只缓存自己那几层的专家,两张卡合起来能装大约两倍的专家。这是流水线并行,不是张量并行——一个 token 每个校验窗口才跨卡一次(几百 KB,走 pinned 内存),不需要 NVLink,插在 x4 或 x1 槽上也能用,每张卡自己的 PCIe 比例单独探测。安装器会列出你的显卡并逐个说明能不能用(RTX 20 系起、单卡 ≥ 8 GB、算力 7.5 是硬门槛),两张最优的会被推荐,装在一张卡上的模型下次启动会问一次要不要改成两张。
官方实测的是一台社区成员的机器:RTX 5080 16 GB + RTX 3090 24 GB,跑 Coder、32K 上下文:
| 配置 | 提示 16K / 28K(token/s) | 解码 散文 / 代码(token/s) |
|---|---|---|
| 单张 5080 | 1,726–2,017 / 1,970 | 83–87 / 88–105 |
| 5080 + 3090,最优切点 K=26 | 2,039 / 2,357 | 84 / 110 |
| 5080 + 3090,自动(K=22) | 2,037 / 2,073 | 80 / 109 |
提示速度比单张 5080 快 18% 到 20%,解码约持平、代码场景快 4% 到 7%。两条反直觉的经验:更快的卡放前面、让它多分几层反而更好;不要加一张明显更慢的第三张卡——塞一张 2080 Ti 进 5080 + 3090 的组合,解码掉到 68 / 90,因为每多一张卡,每个窗口就多一次它自己的往返。文档里也说了正面对的条件:当模型的专家装不进那两张快卡时,加卡才有回报。
四个量化档位怎么选,是配机时另一个要定的事:
| 档位 | 下载 | 内存占用 | 速度 | 质量 |
|---|---|---|---|---|
| Q2_0 | 66 GB | 约 34 GB 专家 + 约 6 GB | 最快 | 好 |
| IQ2_XS | 68 GB | 约 36 GB 专家 + 约 6 GB | 接近 Q2_0 | 更好(作者推荐) |
| IQ3_XXS | 76 GB | 约 43 GB 专家 + 约 6 GB | 慢些(CPU 活更多) | 很好 |
| IQ3_S | — | 内存 + 显存合计 54.8 GB | 最慢 | 作者称在已公布测试上追平完整模型 |
这里有个取舍值得点出来:IQ3_S 是这四个里质量最好的,但输出速度只有 Q2_0 的六成(4K 下 53 对 93 token/s),内存需求还高出 46%。选它等于用一半的速度和更多内存换「不必担心量化损失」。Q2_0 最快最省,质量是「好」。作者自己推荐的是中间的 IQ2_XS:接近 Q2_0 的速度,质量好一点。
另外两条限制值得先说:IQ3_XXS 与 IQ3_S 在 262K 上下文下没有测过——它们的专家区是 43 / 50 GB,260K 的上下文会把 64 GB 的机器顶到内存上限,安装器在 64 GB 机器上会把它们限制在 128K;IQ3_S 只有原始模型版,Swift 1.5 没有这一档。
除这四个之外还有两个变体。
Coder 版本砍掉每层 512 个专家里的一半(保留的 256 个由 RCO 在代码、智能体、视觉数据上选出),每 token 仍激活 10 个;分片一只有 29.6 GB,所以 32 GB 内存就能跑,262K 上下文要 64 GB,代价是作者原话「编码之外更弱」。它的作者报出的数字是 SWE-bench Verified 追平完整模型的 91.3%、LiveCodeBench v6 的 98.7%。名字里的 IQ1_M 指的是「每个原始参数 1.89 位」,保留的专家实际按 IQ3_S 的精度存。它的分片二和视觉编码器直接用原始模型的文件,所以装了原始版之后,安装器只多下一份分片一。它带自己的专家画像。
Swift 1.5 是 UkisAI 的第三方微调版,目标是「想得更短就给出答案」,架构相同、三档尺寸相同、有自己的视觉编码器。其作者称思考 token 少 63%、答案早 1.8 倍、准确率损失不到 1%;Strata 跑它的速度和跑原始版基本一样(4K + IQ2_XS:提示 465 / 输出 78.7,对原始版 467 / 78.3)。作者做了个小测试:8 道推理题两个模型都 8 对 8,Swift 用了 1,234 个输出 token、28 秒,原始版用了 2,682 个 token、46 秒,差距主要来自其中一道题原始版想了 1,524 个 token。它有自己的许可证(Swift Open License 1.0)。
最后两项属于装机时要预留的余量。
首次启动会卡一到三分钟:它要把 34 到 55 GB 读进内存并锁住一部分留给显卡,鼠标可能不响应。这不是故障,README 用加粗写了两遍「等待,不要关窗口」,窗口里也会说明在做什么。第二次启动快得多(文件已在操作系统缓存里),正常启动是 30 到 90 秒。
--calibrate 值得跑一次:有三个参数更取决于机器而不是模型——多少未命中的专家改走 PCIe 拷贝(--pcie-frac)、草稿层要多有把握才多猜一个词(--spec-min-p)、用多少线程算专家(--pool-workers,在有能效核的 CPU 上少一些反而更快)。安装器可以顺手在你这台机器上各测一遍,只保留快过 3% 的设置,约 5 到 10 分钟;作者的测试机上这一步把 Coder 提升了 7%。结果按「机器 + 模型」记住,升级不丢。
这一章讲机制与耗时构成:三件事怎么同时跑、显存那几千个专家位怎么选、草稿怎么来、长提示与长上下文的代价。
一个 48 层的模型,每一层都要跑一次注意力(或递归混合器)、一次路由器、一次专家混合。这三件事被摊到三个资源上同时做:显存负责每 token 必读的稠密权重、注意力和被缓存的几千个专家;CPU 拿着内存里的全部专家,就地计算没上显卡的那些;PCIe 的 DMA 引擎把一部分本该 CPU 算的专家直接从内存搬给显卡,搬运过程不占用任何一方的算力。
三条路径同时跑,不排队。论文里那张 4K 上下文的单轮拆解是理解全局的关键:Q2_0 一轮(一个最多四 token 的校验窗口)总共 34.3 毫秒,其中 CPU 等显卡 14.6 毫秒,显卡等 CPU 算专家 13.4 毫秒,草稿 2.0 毫秒,其余 4.3 毫秒。两边各占一半,互相在等对方。
论文把它列为发现第 1 条,并给出推论:单换任何一个部件都换不来两倍速度,最多改善三分之一。 因为你换掉的那一半最多快一倍,另一半仍是原来的时间,串行总时间只缩短不到一半;何况两边还在互相等待。
专家是均匀摆放的,但路由器挑人的频率极不均匀——有些专家被反复选中,有些几乎没人用。所以显存里那几千个专家位该放谁,本身是个优化问题。做法分两步:启动时按一份在别的提示上录下来的画像(profile)填入最常用的那些;运行中每四轮,把当前对话反复要用的最多 96 个专家,换掉最不常用的那些,而这次替换发生在显卡做草稿的时候。
论文给出的数字(发现第 4 条):在别的提示上录的画像,用 4,500 个显存位能覆盖 50% 的被路由专家;加上按当前对话自适应的替换,命中率抬到约 72%。另一条工程细节(发现第 10 条):替换以前会让下一轮等拷贝,改成「立刻驱逐旧的、新的等拷贝落地再收进来」之后,Q2_0 在 4K 下从 91.7 提到 94.4 token/s。
画像这件事还有两个演进:0.1.14 起画像给全部 24,576 个专家排了序(此前只到 8,000 个,约 10–14 GB);tools/make_profile.py 可以用你自己的提示生成画像——先把引擎跑一次、用 --dump-routing trace.bin 导出路由轨迹,再交给这个工具。
读那 3.5 GB 稠密权重是显卡每一轮的主要开销,而它几乎不随 token 数增长——一次读一 token 和一次读四 token,成本差不多。这就是推测解码的空间。Qwen3.8-Flash-Next 自带一个单层多 token 预测头,GGUF 文件里不含它;Strata 从原始 BF16 检查点里单独取它的张量(5 GB),用 2-bit 存、并把输出头缩到最可能的 40,525 个 token 上,放进显存。
每一轮,上一个被接受的 token 加上最多三个草稿一起穿过全部 48 层,算一个校验窗口;引擎保留主模型同意的每一个草稿,再多留一个它自己产出的 token。草稿只有在 MTP 层至少有 50% 把握时才进窗口。结果(论文发现第 3 条):输出与纯贪婪解码逐 token 完全一致,他们用故意写错的草稿和回滚验证过。论文还给了一个更硬的对照——与 llama.cpp 相比,下一个 token 分布的平均 KL 散度是 0.022,而 llama.cpp 自己的 CPU 版与 GPU 版之间的差异是 0.058,也就是说这套推测解码引入的偏差比同一个引擎换后端跑还小。
实测收益 1.6 到 1.8 倍:4K 上下文下,不用推测解码是 47 到 57 token/s,用了之后是 82 到 92;窗口平均一次产出 2.4 到 3.2 个 token。论文解释了为什么这个倍数低于「整个模型都在显存里」的引擎报出的 2.5 到 3 倍——窗口里每多一个 token,就要多路由一次它自己的 CPU 专家。
另有一个针对特定文本形状的补充机制(0.1.7 的提示查找):当回答需要重复上下文里的内容时(改代码、引用原文),它从前面那一份里直接取最多 5 个 token 当草稿,但只在实测接受率与成本划算的地方才用。作者给的数字是改代码快 6% 到 11%,其他文本没有变化;这些草稿走相同的校验流程,输出依然一致。
这是全套引擎里单次收益最大的一次改动。此前提示是分成 2,048 个 token 一块处理,之后改成最多 8,192 块,基准记录把六个步骤逐条拆开:
缓冲区合并(注意力和 MoE 共用一块暂存,每个提示 token 省约 260 KB)是逐位等价的;下一块的 PLE 行改用线程从模型文件里提前读,762 变 790;块大小改成自动选(8,192 能塞下就用 8,192),Q2_0 从 791 涨到 973;没能钉住的内存页改由辅助线程拷贝,IQ3_S 在 4,096 块下 551 变 652;PLE 块改成整块算而不是逐 token 算,Q2_0 981 涨到 1,053;最后两步最关键——专家乘法改用 llama.cpp 的 MMQ 内核(权重保持量化,走 int8 tensor core),而不是先展开成 FP16 再交给 cuBLAS,Q2_0 1,052 涨到 1,130;以及让下一层的专家在上一层注意力算的时候就通过 PCIe 流进来,Q2_0 最终 1,130 涨到 1,290。
同一份记录里还留着一条「试过但放弃了」的:用 cuBLAS 的分组 GEMM 做专家乘法,孤立测更快,放进引擎里反而慢(770 对 790)。
净效果:32K 提示从 572 token/s 涨到 1,290,IQ3_S 从 383 涨到 1,208。走服务端口径(Q2_0、128K 上下文)一致:999 token 的提示从 353 到 438,6,927 token 从 529 到 1,077,28,584 token 从 584 到 1,249。输出速度不变。
质量方面,作者用 32K 提示走完提示路径后,把最后 256 个 token 丢回逐 token 路径并导出 logits 做对比,发现「换块大小」这种纯工程改动本身就会让预测产生相近幅度的偏移,以此作为衡量标尺;MMQ 那一步的平均 KL 是 0.38,与标尺的 0.33 同量级。长文档针尖测试(把答案藏在某个深度)在 1K、32K、128K、262K 上都是 5 对 5。
这里有个看起来矛盾、其实不矛盾的地方。DETAILS.md 的「How it works」一节至今写着提示按 2,048 token 分块,而同一份文档讲 0.1.13 的一节写的是「最多 8,192」。两处都对,说的是不同场景:单卡时安装器写进配置的是 --prefill auto(setup.py 注释写「up to 8192,视空闲显存而定,长提示大约快一倍」),而 MULTI_GPU.md 明确说多卡时每张卡都有自己的提示缓冲区、默认仍是 2,048 块(每卡 1.5 GB,--prefill 1024 减半),因为每张卡都要留一份。
上下文超过 8K 之后,键值缓存改用 8 位整数存储。MTP 层只关注最近 32,768 个位置,所以它在 262K 上下文下做草稿依然很便宜。真正的代价是显存:在 262K 时,那 12 层稀疏注意力的键值缓存、它们的索引器键、以及更大的提示缓冲区加起来约占 4 GB,自动分配只剩约 1,600 个专家位,而不是 4,500 个。结果就是 Q2_0 的解码从约 95 token/s 掉到约 56。论文把这件事总结成发现第 12 条:长上下文吃的是显存,不是算力。
后面几个版本给了三个旋钮,各自标了代价:
KV 流式(0.1.5):64K 以上把上下文的 KV 缓存放进内存,只把注意力真正要读的那一段留在显存(--kv-resident 32768),腾出来的显存给专家。Q2_0 在 262K 下从 50.9 涨到 62.6 token/s,显存里的专家从 1,589 个变成 3,872 个;128K 下大约 +6%。它读的是完全相同的值,只是 KV 存放位置变了;代价是每个上下文 token 多花约 13.7 KB 内存(128K 时约 1.7 GB)。
可选 4-bit KV(0.1.8,PR #21):先做一次 Hadamard 旋转再取 4 位(正交的 256 点 Walsh–Hadamard 旋转,K、V、Q 都转),KV 缓存内存减半(每格每层 576 字节,对 int8 的 1,056 字节),128K 下约快 4%;代价是长文档精度可测量地变差(困惑度 +8% 到 12%),针尖测试仍通过。所以 8 位是默认。
混合 K8V4(0.1.25,PR #120):键保持 8 位、值存成旋转后的 4 位,比 8 位省 23% 的 KV 内存。RTX 3090 跑 Coder、198K 上下文,输出从 85 涨到 99 token/s,针尖结果不变,代价是提示慢 2% 到 5%。它不参与流式,所以主要受益的是「大卡 + 长上下文」这个组合。
等待时间比速度数字更贴近体感:用 Q2_0,读到第一个字大约是 4K 提示 4 秒、32K 25 秒、128K 不到 2 分钟、262K 约 4.5 分钟。
论文那 13 条发现,前面已经用掉第 1、2、3、4、6、7、8、9、10、11、12 条;剩下几条都是「先试错、再结论」的样本,数值本身也有参考价值。
把每个窗口拆成两组 token 让 GPU 和 CPU 交错,理论上优雅,实测反而慢 7%(85.8 对 91.8 token/s),因为稠密权重被读了两次、每组共享的专家还更少——这条被明确放弃(发现第 5 条)。发现第 13 条是平台细节:Windows 上不能把 34 到 43 GB 当成一整段去锁页,得按层分段加一个工作集锁,并且一次 PCIe 读不能跨两段(日志里那句 cudaHostRegister ... out of memory 是正常的,引擎会改成按层分段钉住)。发现第 11 条讲的是提示速度对上下文长度基本平坦(论文当时是 4K 539、262K 496),这也是 0.1.13 之后长提示能做到一两千 token/s 的原因——分块预填把算力用满了,不受上下文长度惩罚。
这一章讲接口:三个页签、两套协议、思考档位、MCP 工具、图像、会话缓存,以及跨设备访问要先做什么。
服务起来之后,浏览器打开 http://127.0.0.1:8080 是它自带的应用(模型启动时它自己会打开),三个页签:
Chat——流式回答、思考过程答完自动折起、代码带复制按钮、有图像时可以直接贴图,以及采样与思考档位设置;对话只留在浏览器里。http://127.0.0.1:8080/?q=你的问题 可以直接带着一个新对话打开。
Monitor——模型此刻在做什么(在读提示、进度多少、还是在写答案、多少 token/s);显卡负载、显存、温度、功耗与 PCIe 流量;CPU、内存、磁盘;当前上下文用量;最近的请求。每个请求会标出它走的是实验性投影还是原版。
About——模型与引擎设置,以及给别的 App 用的接入地址。
终端里也可以直接聊:.venv\Scripts\python chat.py(Linux 是 .venv/bin/python chat.py),在里面用 /image <路径> 附图、/think low 切思考档位。
它同时实现两种协议。支持「OpenAI 兼容」的客户端,base URL 填 http://127.0.0.1:8080/v1、API key 与模型名随便填即可;用 Anthropic 协议的客户端走 http://127.0.0.1:8080/v1/messages;Claude Code(需要 Strata 0.1.17 以上)只要设 ANTHROPIC_BASE_URL=http://127.0.0.1:8080,并把 ANTHROPIC_MODEL 填一个它能认的 Claude 名字(引擎会忽略这个名字)。完整端点表:
| 端点 | 返回什么 |
|---|---|
POST /v1/chat/completions | OpenAI 协议,流式与非流式,支持工具调用 |
POST /v1/messages | Anthropic 协议,流式与非流式,支持工具调用 |
GET /v1/models、GET /models、GET /health | 模型列表与健康检查 |
GET /props | 原始聊天模板、上下文上限、生成默认值、引擎版本 |
GET /status、GET /slots | 此刻在读提示、在回答,还是空闲;已生成多少 token |
GET /metrics | Monitor 页签显示的全部内容 |
GET /mcp | MCP 服务器列表、状态与工具 |
有 API key 时,这些元信息端点与 /slots 也要求带上 key。它们不会加载、卸载或重启模型。/props 里的「上下文」指的是引擎的完整上下文,不是常驻的那段 KV 窗口;n_predict: -1 表示输出不设上限。
思考档位四档:none、low、medium、high,默认 high。两套协议各有各的写法:
| 协议 | 怎么写 |
|---|---|
| OpenAI | reasoning_effort 取 none / low / medium / high;也可用 reasoning.effort,或 chat_template_kwargs.enable_thinking: false |
| Anthropic | output_config.effort 取 low / medium / high;或 thinking.type: disabled;或 thinking.type: enabled 加 budget_tokens(低于 2K 算 low,低于 8K 算 medium,再多算 high) |
作者注明这不是硬性 token 上限,而是模型被训练过的指令:简单问题上三档都只想一小会儿,难问题上 high 想得最久也最准。none 立刻回答,最快。
采样参数按请求生效:temperature、top_p、top_k、min_p、seed,两套协议的字段名都认。三条要注意:top_k 最多保留 64 个候选(写 0 或超过 64 都用 64);默认的自适应专家档位下,采样结果不可复现——要 seed 可复现就把 --adapt-every 100000 加进引擎参数(等于关掉自适应、改成静态常驻);惩罚项(presence_penalty、frequency_penalty、repetition_penalty)从 0.1.19 起会作用到推测解码一次校验的每一个 token 上(此前只有每批第一个吃到),这让带惩罚项的请求比 0.1.18 慢 1% 到 11%,不带惩罚项的请求不受影响。
MCP 工具:在 strata-<model>.json 里按 Claude Desktop 的格式写 mcp_servers(可以原样粘贴 Claude Desktop 的配置,键名 mcpServers 也认),聊天页就能把那些工具交给模型用。
"mcp_servers": {
"files": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\me\\Documents\\notes"]},
"search": {"url": "http://127.0.0.1:3000/mcp", "headers": {"Authorization": "Bearer ..."}}
},
"mcp": {"timeout_s": 60, "max_result_chars": 20000, "max_rounds": 8}
程序类服务器(command + args)由 Strata 启动、走 stdin/stdout 说话,npx 需要装 Node.js;地址类(url)走 MCP 的 Streamable HTTP,老的只支持 SSE 的传输不支持。服务器随 Strata 在后台启动,窗口里会打印每个服务器提供了什么(MCP server 'files': 14 tools (...)),启动失败的会被跳过并说明原因、聊天照常。工具调用在聊天里显示为一个小块(工具名、参数、结果),模型读完结果继续,一次回答里最多连续调用 8 轮;超时默认 60 秒,结果超过 20,000 字符会被截断并注明;停止按钮也会停掉正在跑的工具。只有聊天页用它们——API 客户端(各种 SDK、编码助手)看到的行为不变,用的是它们自己的工具;一个发往 /v1/chat/completions 的请求可以带 "strata_mcp": true 主动加入。
文档里的安全边界写得很直白:MCP 工具以你的用户权限在你的电脑上运行,而且由模型决定何时调用——包括它读到的内容(网页、文件里都可能藏着指令)触发的那种调用。给文件系统服务器只开需要的目录,优先只读工具,不要加你不信任的服务器。工具只能从聊天页本身调用(带别的站点 Origin 或不带 JSON 内容类型的请求会被拒);如果 Strata 能从别的设备访问,一定要设 key。
图像是可选的:安装时回答「要」,或之后用 --vision gpu 再跑一次。编码器是一个 0.9 GB 的 27 层 ViT 加投影层,它把图片变成与词嵌入同宽的行,放到提示里 <|image_pad|> 的位置并给每个位置一个二维坐标(行列,模型用交错的 M-RoPE);在这套测试图上,答案是逐 token 与 llama.cpp 的多模态实现一致的。
| 编码器放在 | 每张图耗时 | 代价 |
|---|---|---|
| 显卡(推荐) | 0.1–0.5 秒(最多 1,024 个图像 token) | 常驻约 1.4 GB 显存,专家缓存少约 1,000 格,纯文本慢几个百分点 |
| CPU | 10–30 秒(图片会缩到约 300 个图像 token) | 不占显存 |
同一台机器(RTX 5070 12 GB)上开图像对文本速度的影响:Q2_0 提示 541 → 531、输出 90.7 → 86.9;IQ2_XS 468 → 458、77.1 → 74.0;IQ3_XXS 411 → 401、64.7 → 59.5。一张 640×480 的照片约等于 300 个图像 token,同一张图重复发送只会编码一次(聊天客户端每一轮都重发图片,这一条很实用)。格式支持 JPEG、PNG、BMP、GIF、WebP、TIFF、AVIF(后几种先转成 PNG)。文档里给了一个具体例子:一张 640×480 的报纸页面,问它上面写了什么,Q2_0 用 1.9 秒答出「MEN WALK ON MOON」,其中编码约 0.1 秒,提示 207 token/s、输出 65–73 token/s。
跨设备访问必须先设 key。默认只监听 127.0.0.1,要让局域网里别的设备用,得同时给 --host 0.0.0.0 和一个 API key,并在防火墙放行 8080(Windows 会弹提示,同意给 Python 即可;也可以管理员 PowerShell 里加一条 New-NetFirewallRule)。要让公网访问,标准做法是前面挂一个隧道(文档举的是 cloudflared tunnel --url http://127.0.0.1:8080),但先把 key 设上,否则拿到链接的人都能用你的电脑。
会话缓存做得比预期细:最多留 6 个检查点(每个约 118 MB),在每个新的助手回合开始处、以及每 16K 提示 token 处各取一个。一个检查点只有在「新提示严格以它的 token 和图片开头」时才能用。最老的那一个——实际上是系统提示的结尾,也就是同一个客户端每个对话都共享的那段——会被长期保留,其余的按最近最少使用轮换,所以全新对话若共享这段前缀,不必从第 0 个 token 重读。另外,一个从零读起的提示,只要它的系统提示不短于 2,048 token,也会在系统提示结尾处存一个检查点(0.1.20,PR #62 与 #65),这样对系统提示和工具列表很长的助手类客户端,那个「根」就一定存在。引擎侧的旋钮是 --prompt-cache N(0 表示关)、--prompt-cache-every N、--prompt-cache-root N、--turn-token ID。
两件用起来才会碰到的限制。一是一次只处理一个请求,KV 缓存里同时只装一个对话的历史——在两个对话之间切换会重读分叉点之后的部分(共享的系统提示部分可以复用)。二是上下文超限时从不静默截断,直接返回 400;max_tokens 会顶出上下文的请求同样被拒。这个设计在 API 语义上是对的,但会和各家编码助手的自动压缩机制打架,第八章 #145 讲的就是这件事;让步开关是在配置里加 "fit_max_tokens": true(或给 serve/server.py 加 --fit-max-tokens),让引擎把输出上限收缩到上下文剩下的空间。完全不剩空间的提示仍然会被拒。
还有一条容易被忽略的:关闭连接或在 App 里按停止是真的会停下模型,所以下一个请求可以立刻开始。读长提示期间,流式连接会持续发 keep-alive,避免助手端超时;服务端窗口每 15 秒打印一次进度,GET /status 也会说它此刻在做什么。
从 2026 年 9 月 24 日建库到 9 月 30 日,引擎版本号从 0.1.0 走到 0.1.27。
值得记下来的不是「更新快」本身,而是它的记录方式:每一次性能改动都对应一个带日期的基准目录,里面有原始 JSON 和一份写清楚「改了什么、数字从多少到多少」的说明。把 12 组摊开是这样(中列是该目录 README 自己写的主题):
| 目录 | 记录了什么 |
|---|---|
2026-09-24-final | 首发日完整基准矩阵与全部显卡估算(matrix.json + estimates.json) |
2026-09-24-vision | 图像支持打开后对文本速度的影响 |
2026-09-27-cache-parity | 专家缓存的等价性检查(针对 issue #23 那个「GPU 命中路径不正确」的警告) |
2026-09-27-esp | 实验性速度投影的 KL 与困惑度测量(附 esp_kl.py、esp_serve.py 与原始 teacher-forced.txt) |
2026-09-27-kv-q4 | 可选 4-bit KV 的精度代价(困惑度 +8–12%) |
2026-09-28-coder | Coder 版本的基准矩阵 |
2026-09-28-prefill-speed | 0.1.13 六步预填优化的逐步拆解(第四章引用的就是这一份) |
2026-09-28-speed-0114 | 0.1.14 的速度基准 |
2026-09-29-layer-split | 层切分的收益(提示 +18–20%,K=26 最优) |
2026-09-29-layer-split-limits | 层切分下哪些功能跨卡可用、哪些不能 |
2026-09-29-speed-0122 | 0.1.22 的速度基准 |
2026-09-29-speed-0126 | 0.1.26 的速度基准,本文引用作者数字的主要来源 |
最近这一组(0.1.26)的 README 把收益来源逐条写了出来:相对 0.1.22,32K 到 128K 的提示速度提升 8% 到 28%(Q2_0 在 32K 从 1,844 到 2,171,128K 从 1,682 到 2,107;Coder 在 128K 从 1,779 到 2,208),这部分提升来自三个不同版本——0.1.24 把 QSA 选择放到张量核上、0.1.25 把分组表放进映射内存并融合了归一化、0.1.26 把草稿层的一次批处理做了。
收官版本 0.1.27 的提交信息是三件事:
Engine 0.1.27: CJK answers faster (the draft subset with Chinese, Japanese
and Korean), RTX 20 cards, the HIP build without CUDA headers; setup requires it
中文答案更快、RTX 20 系加入支持、HIP 构建不再需要 CUDA 头文件。第一件事直接来自社区报告——下一章会讲到那条链怎么闭上的。
这一章把测量口径摊开:一次「请求」指什么、哪些数字能横向比、哪些不能。
一张速度表如果不写口径,就没法用。Strata 的文档在这件事上写得相当完整,先把口径抄下来。
作者那台机器:RTX 5070 12 GB(PCIe 5.0 x16)、Ryzen 5 7600(6 核)、64 GB DDR5-5200、Windows。表里的数字来自引擎 0.1.26,用的是安装器自己写进配置的那套设置:--prefill auto、4K 以上用 8 位 KV、64K 起开 KV 流式。
一次「请求」是:每个长度一个「代码智能体」风格的提示,生成 256 个 token,开启 MTP 推测解码。表里的「262K」不是 262,144,而是模型完整的上下文窗口,实际提示长度 259,943 token。IQ2_XS 那一行是用 Swift 1.5 的 IQ2_XS 测的——它跑得与原始版一样快。
速度是谁报的:都是引擎自己报的值(decode 计时),不是墙钟估计;bench/ 里的原始 JSON 记的就是这些。这一点在比较社区数字时很重要——截图上的 token/s 常常是墙钟算出来的,会把读提示、排队、首 token 延迟都算进去。
提示速度(token/s,0.1.26,同一台机器):
| 模型 | 1K | 4K | 32K | 64K | 128K | 262K |
|---|---|---|---|---|---|---|
| Q2_0 | 536 | 1,299 | 2,171 | 2,126 | 2,107 | 1,304† |
| IQ2_XS | 534 | 1,256 | 2,092 | 1,754 | 1,752 | 1,181*† |
| IQ3_XXS | 482 | 1,007 | 1,745 | 1,609 | 1,602 | — |
| IQ3_S | 427 | 913 | 1,624 | 1,640 | 1,443 | — |
| Coder | 656 | 1,583 | 2,177 | 2,236 | 2,208 | 1,034** |
† 这一次没有重测,数字来自 0.1.22。\* 这一行开着图像(编码器的显存预留让能缓存的专家少了一些)。\*\* 这一次没有重测,数字来自 0.1.14。
输出速度(token/s,同一批运行):
| 模型 | 1K | 4K | 32K | 64K | 128K | 262K |
|---|---|---|---|---|---|---|
| Q2_0 | 87.3 | 93.0 | 81.8 | 76.2 | 73.7 | 60.3† |
| IQ2_XS | 79.6 | 78.6 | 76.3 | 63.7 | 62.7 | 52.8† |
| IQ3_XXS | 61.9 | 61.6 | 58.5 | 57.2 | 49.0 | — |
| IQ3_S | 52.4 | 53.3 | 48.3 | 46.3 | 45.5 | — |
| Coder | 58.9 | 55.1 | 54.9 | 53.2 | 43.0 | 42.8† |
这张表里有三点和直觉不一样,都是口径问题:
一、提示速度随上下文反而先升后降。 1K 时只有 536,32K 时到 2,171,128K 还有 2,107,262K 掉到 1,304。原因是提示走的是分块预填,块内是大规模并行,块越大越能把 GPU 喂饱;1K 的提示塞不满一个块,所以在「热身」。这也解释了文档里那句提醒:短提示跑不到 4K 的速度,2,048 token 一块才是提示路径效率最好的地方。
二、输出速度随文本内容浮动,同一个提示两次也能差几个百分点。 因为推测解码的速度取决于草稿被接受的比例,而接受比例取决于模型写什么。作者给的例子是同一个 4K 提示连着跑:0.1.14 是 88.5、0.1.12 是 85.7。所以拿两次不同回答的「最后一次请求」去比版本,能差出 20% 到 30%。
三、长上下文掉的是输出速度,不是提示速度。 128K 时提示还有 2,107,输出掉到 73.7;到 262K,输出 60.3。原因在第四章:上下文越长,KV 缓存与索引器占的显存越多,能缓存的专家位越少。
最后是版本边界,这一条最容易踩。 论文里的提示处理速度是 4K 539 token/s,而同一台机器上 0.1.26 的 4K 是 1,299——差两倍多。这不是性能提升也不是回退,而是两次测量之间隔了一个把长提示优化翻倍的版本(0.1.13)。同理,第三章那张「其它显卡估算」表里的提示数字全部早于 0.1.13,文档自己也注明「这些仍然是比较旧的估算」。拿论文的数字去和今天的版本比会得出完全错误的结论,反过来拿今天的数字质疑论文也不公平。
以下是 99 个 issue 里最有用的八条,全部是用户自述,方法各不相同,与作者基准表不能直接比。表中只列事实,评价留给你。
| issue | 报告的事 | 机器与配置 | 关键数字 |
|---|---|---|---|
| #137 | 随包草稿词表几乎没有汉字 | RTX 5070 Ti 16 GB · Ryzen 7 7700 · 2×48 GB · Ubuntu 24.04 · 0.1.24 | 去掉词表解码 +14%;换成 CJK 扩充版 +45.4%(真实英译中) |
| #165 | 双卡基准(先向作者要了官方 harness) | EPYC 7453 客户机 · 20 vCPU · 165 GiB · 双 RTX 3090 · 0.1.26 | 代码解码 109.7 token/s |
| #178 | 把引擎移植到 AMD RDNA4 | Ubuntu 24.04 · ROCm 7.14 · Radeon AI PRO R9700 32GB | 提示 62.7 / 生成 44.7 token/s;MTP 接受 12/14 |
| #149 | 一个 fork 带回十六个性能与精度改动 | RTX 5090 · Ryzen 9 9950X3D · 128 GB · IQ3_S · 262K | 提示 +42% / +49%;解码 +5% 到 +15% |
| #127 | 不走层切分,三张卡各起一条车道 | 三张 RTX 5070 Ti 16 GB,宿主专家区用 MAP_SHARED 共享 | 引擎自报 78.8 / 78.4 / 80.1,合 237.3 |
| #119 | 「0.1.13 比 0.1.21 快 50%」的疑云 | RTX 3090 | 复测 73.1/76.2 对 73.8/74.3;另一台 RTX 3060 12GB 约 30 token/s |
| #145 | 上下文上限与编码助手的压缩机制打架 | 多家助手客户端(pi、OpenCode CLI) | 换来 fit_max_tokens 这个开关 |
| #143 | agent 循环里会话缓存没被复用 | RTX A4500 20 GB · Xeon w3-2435 · 377 GB · IQ3_S · 0.1.24 | 150K–168K 的提示每轮重读,约 100 秒以上 |
<p>八条里有两条值得单独说清楚它们各自是什么形态,因为这关系到怎么读它们的数字。#165 是唯一一份主动对齐了口径的第三方基准——报告者先向作者索要官方的测试提示与 harness,好让自己的数字能和作者表比;他在报告里也主动标注了「物理 DIMM 布局与内存带宽尚未测量」。#127 则是三条车道的自报值相加,报告者自己写明「不是同步过的墙钟基准」,并且明确说这不一定适合合并进上游。</p>
<p>另外六条里,三条是移植或分支(#178 到 AMD RDNA4、#149 到 RTX 5090、#127 到多进程车道),两条是把引擎当后端接进助手时暴露的协议摩擦(#145、#143),一条是一次口径复核(#119)。它们的共同点是:都不在作者那台机器上,也都不在作者那套设置下,所以横向比较只在同一份报告内部成立。</p>
一、它只服务一个模型。 Qwen3.8-Flash-Next 是唯一被优化的对象(还有它的 Coder 裁剪版和 Swift 1.5 微调版)。换一个模型,这套优化基本作废。这也是它快的原因:通用引擎要照顾所有模型和硬件,它不用。
二、CUDA 优先。 AMD 那条路是实验性的,官方列出的功能缺口是:只支持一张卡、不支持图像、不支持校准、Monitor 不显示显卡统计;安装走 AMD 的 TheRock wheels(约 10 GB,不需要 sudo),引擎在本机编译 10 到 20 分钟。文档里还有一句:「不声称跨后端的模型答案是逐位相同的。」Intel 显卡、Apple 芯片都不在支持范围内。WSL 算半个:能用,但 NVIDIA 驱动在那里只钉得住约 1 GB 内存,所以 KV 流式会关掉、KV 缓存只能留在显存、专家要从非钉住的内存拷到显卡,提示速度低于原生 Linux。
三、一次只处理一个请求。 KV 缓存里一次只装一个对话的历史,两个对话间切换会重读分叉点之后的部分(共享的系统提示可以复用)。要并发,目前的路子是像 #127 那样开多个进程、配多张卡——一个请求一条车道。论文第七章把「在请求之间保留对话状态」列为目前单项收益最大的改进方向:今天每个请求都要重新处理它的整个提示,把每回合结束时的循环状态(几百 MB)和 KV 缓存存下来,可以把一次 30 秒的长对话重读变成零点几秒。issue #57 在跟踪多会话缓存与保存/恢复。
四、速度和质量的取舍是明码标价的。 最快的是 2-bit 的 Q2_0,质量最好的是 IQ3_S,两者在同一张卡上差出三成以上的速度,内存需求差 46%。Coder 版本砍掉一半专家换取 32 GB 内存可跑,代价是作者原话「编码之外更弱」。两个可选 KV 方案各有精度代价:4-bit KV 在长文档上困惑度 +8% 到 12%;混合 K8V4 的代价是提示慢 2% 到 5%。
五、项目极新,而且变得很快。 六天二十七个版本意味着接口和数据格式都可能变;README 自己写着 0.1 版是「一个早期但完整的发布」。本文引用的所有数字都绑定在 v0.1.27 这个快照上,换一个版本就可能不一样。文档里也留着一批「未测」的空白:IQ3_XXS 与 IQ3_S 的 262K 没测,AMD 后端的长上下文压力、答案质量的广泛等价性、其它 AMD 卡、混合厂商推理都未验证。
六、几个很具体的坑。 第一次启动会把 34 到 55 GB 读进内存,机器卡一到三分钟,鼠标可能不响应。用 Windows 任务计划程序开机自启时,由于它的默认设置(优先级「低于正常」、不勾选「以最高权限运行」),专家加载速度会从约 1.42 GiB/s 掉到 0.05 GiB/s——作者在 RTX 5070 Ti + Ryzen 7 9800X3D + NVMe 上量过:
| 启动方式 | 专家加载 |
|---|---|
| 双击 / 终端 / SSH | 1.42–1.52 GiB/s(约 35 秒) |
| 任务计划程序默认设置 | 0.05 GiB/s(821–841 秒) |
| 任务计划程序改过两处设置后 | 1.42 GiB/s(35 秒) |
同样的二进制、同样的参数、同样的缓存状态,差 24 倍;要改的是「优先级:正常」和「以最高权限运行」两处(后者还关系到 Windows 大页需要的 SeLockMemoryPrivilege 权限)。另外:AVX-512 的 CPU 上 Q2_0 会额外写一份约 40 GB 的专家副本;Windows 页面文件小于 4 GB 会出问题(引擎从 0.1.19 起改为减小缓存重试,而不是直接停;安装器会对小于 4 GB 的页面文件给出警告);需要 70 到 80 GB 空闲磁盘,NVMe 强烈建议。
七、关于数字本身。 README 和论文里的性能数字来自作者同一台机器,到本文写作时没有看到独立的第三方复测(#165 是最接近的一份,但它测的是不同的硬件组合,且报告者自己标注了内存拓扑未测)。第三方数字目前都来自社区 issue,方法各异。作者自己也反复提示输出速度会随「模型写什么内容」浮动几个百分点。
八、那个「实验性速度投影」要单独说。 它是 data/experimental-speed-projection/ 下一个 483,520 字节的控制向量,出厂默认关闭,只在安装时问一次(默认否),且只对原始模型有效,Swift 1.5 不行。它做的事是在第 4 到第 44 层之后(共 41 层被引导),从残差流的每一条超连接上减掉一个方向(h -= (h·v)v),每层一个单位向量,和 llama.cpp 的 --cvec-mode project 补丁一样。该向量的包自己把它描述为「拒绝方向投影」:打开之后模型拒绝的请求大幅减少(它报的是测试集上 50 个里从 50 个拒绝降到 1 个)。
作者的措辞很清楚:去掉拒绝就是去掉一种安全行为,打开它之后模型写什么由你负责。 它也不是优化——同样的文本上每个 token 多花 0.2% 到 0.4%,聊天里 token/s 会怎么变取决于模型写的文本(长度、重复、草稿命中率),所以要拿自己的提示去量。作者自己测的数字(Q2_0、固定专家、2,557 个教师强制 token,含代码、文档与闲聊):top-1 token 在 10% 的位置发生变化,与原始模型的平均 KL 是 0.063 nats(最大 4.1),代码上的困惑度上升 15%、文档 +2.3%、闲聊 +0.4%。请求体里给 "experimental_speed_projection": false 可以关掉单个请求的它(配置里放 "sampling": {"experimental_speed_projection": false} 改默认),关掉之后输出与原始模型逐 token 一致;切换会丢弃一次会话缓存,因为模型状态是按另一种方式算出来的。Monitor 会给每个请求标出它走的是 ESP 还是原版。
九、版本边界会咬人。 前面第七章已经摊开过:论文的提示速度(4K 539 token/s)早于 0.1.13,0.1.26 的同项是 1,299,差两倍多。这不是性能提升也不是回退,是两次测量之间隔了一个把长提示优化翻倍的版本。同理,论文里那些其它显卡的估算数字(标注 ±20%)全部早于 0.1.13。
| 数字 | 值 | 来源 | 级 |
|---|---|---|---|
| 仓库规模 | 223 个文件 / 56,357 行(不含 third_party/) | 本地清点 | 一手 |
| 引擎本体 | src/ 123 个文件 / 45,692 行 | 本地清点 | 一手 |
| 内核分布 | kernels/ 82 个文件 / 26,679 行,CUDA 44、CPU 11 | 本地清点 | 一手 |
| 主循环 | src/program/generate.cpp 5,116 行 | 本地清点 | 一手 |
| 服务端 | serve/ 6 个源文件 / 3,381 行,server.py 1,784 行 | 本地清点 | 一手 |
| 工具链 | tools/ 19 个 Python 脚本 / 4,023 行 | 本地清点 | 一手 |
| 文档正文 | 22 个 Markdown / 1,985 行,DETAILS.md 603 行 | 本地清点 | 一手 |
| 基准记录 | bench/results/ 12 组、73 个文件 | 本地清点 | 一手 |
| 工作树体积 | 15.5 MB(克隆含 .git 27.4 MB) | 本地清点 | 一手 |
| 社区体量 | 1,901 star / 215 fork / 99 issue / 93 PR | GitHub API(2026-09-30) | 一手 |
| 模型结构 | 48 层、24,576 专家、每 token 激活 10 个 | 论文 §2 | 一手 |
| 字节普查 | 专家 34.0 GB,每 token 读 0.66 GB(2%) | 论文表 1 | 一手 |
| 显存常驻 | 稠密部分约 3.5 GB | 论文表 1 | 一手 |
| 单轮拆解 | 34.3 ms = GPU 14.6 + CPU 专家 13.4 + 草稿 2.0 + 其他 4.3 | 论文表 5(4K,Q2_0) | 一手 |
| 推测解码收益 | 1.6–1.8×(47–57 → 82–92 tok/s) | 论文发现 2 | 一手 |
| 推测窗口产出 | 平均 2.4–3.2 个 token | 论文发现 2 | 一手 |
| 与 llama.cpp 的偏差 | 平均 KL 0.022,低于其自身 CPU/GPU 差异 0.058 | 论文发现 3 | 一手 |
| 专家缓存命中率 | 静态画像 50% → 自适应 72% | 论文发现 4 | 一手 |
| 组内交错 | 反而慢 7%(85.8 对 91.8 tok/s) | 论文发现 5 | 一手 |
| CPU 侧读权重速率 | AVX-512 内核约 42 GB/s;内存并发 41、单独 52 GB/s | 论文发现 6 | 一手 |
| i-quant 的算力上限 | 每核约 5 GB/s,六核 23–26 GB/s | 论文发现 7 | 一手 |
| 显存槽位按字节算 | 2,673 → 3,556 个专家,1K 解码 +13% | 论文发现 8 | 一手 |
| PCIe 拷贝分配 | i-quant 55%(70→78 / 56→65),Q2_0 20% | 论文发现 9 | 一手 |
| 无等待换血 | 91.7 → 94.4 tok/s(4K,Q2_0) | 论文发现 10 | 一手 |
| 提示速度对上下文 | 论文口径 4K 539、262K 496(早于 0.1.13) | 论文发现 11 | 一手 |
| 长上下文代价 | 262K 时专家位 4,500 → 约 1,600,解码 95 → 56 | 论文发现 12 | 一手 |
| Windows 锁页 | 按层分段 + 工作集锁,一次 PCIe 读不跨段 | 论文发现 13 | 一手 |
| 提示提速(0.1.13) | 32K 提示 572 → 1,290(Q2_0),IQ3_S 383 → 1,208 | bench/…/prefill-speed | 一手 |
| 0.1.26 对 0.1.22 | 32K–128K 提示快 8–28%(32K 1,844→2,171) | bench/…/speed-0126 | 一手 |
| 完整提示速度表 | 1K–262K × 五个档位(见第七章) | DETAILS §Speed(0.1.26) | 一手 |
| 完整输出速度表 | 1K–262K × 五个档位(见第七章) | DETAILS §Speed(0.1.26) | 一手 |
| 作者基准(0.1.26) | Q2_0:4K 输出 93.0 / 128K 73.7 | README 与 DETAILS 表 | 作者自测 |
| 首字延迟(Q2_0) | 4K 约 4 s、32K 25 s、128K <2 min、262K 约 4.5 min | DETAILS | 作者自测 |
| 四档内存需求 | 37.6 / 39.2 / 47.0 / 54.8 GB | README 选型表 | 作者自测 |
| 其它显卡估算 | 5060 Ti / 3090,±20%,且全部早于 0.1.13 | 论文表 6 与 DETAILS | 估算 |
| 层切分收益 | 提示 +18–20%,K=26 最优;第三张慢卡反而拖慢(68/90) | bench/…/layer-split | 作者自测 |
| 任务计划程序陷阱 | 1.42–1.52 GiB/s → 0.05 GiB/s(约 35 s → 821–841 s) | DETAILS §Windows | 作者自测 |
| KV 流式 | 262K 下 Q2_0 50.9 → 62.6;专家 1,589 → 3,872 | DETAILS | 作者自测 |
| 4-bit KV 的代价 | 长文档困惑度 +8–12%,128K 约快 4% | bench/…/kv-q4 | 作者自测 |
| K8V4 收益 | KV 内存 −23%;3090 + Coder + 198K:85 → 99 tok/s | DETAILS / PR #120 | 作者自测 |
| 低内存模式 | Coder 提交内存 36 GB → 约 13 GB | DETAILS | 作者自测 |
| 图像开销 | 常驻约 1.4 GB 显存;Q2_0 输出 90.7 → 86.9 | bench/…/vision | 作者自测 |
| 实验性投影的代价 | 每 token +0.2–0.4%;代码困惑度 +15%;平均 KL 0.063 | bench/…/esp | 作者自测 |
--calibrate 收益 | 作者的测试机上 Coder 提升 7% | README | 作者自测 |
| 中文词表缺陷 | 40,525 中仅 27 个含汉字;全覆盖 55,328 | issue #137 | 用户实测 |
| 中文任务提速 | 去掉词表 +14%;换成 CJK 扩充版 +45.4% / +7.1% / +34.8% | issue #137 | 用户实测 |
| 中文词表已修复 | HEAD a790805 主题即中日韩草稿子集;tools/draft_vocab.py 已在仓库 | 本地读 HEAD 提交信息 | 一手 |
| 与 llama.cpp 对照 | 提示 2,590–2,830 对 660–750;128K 解码 79.7 对 81.3 | issue #137 | 用户实测 |
| 双 3090 基准 | 代码解码 109.7 tok/s | issue #165 | 用户实测 |
| AMD RDNA4 | 提示 62.7 / 生成 44.7 tok/s;CTest 28/31 | issue #178 | 用户实测 |
| RTX 5090 fork | 提示 +42% / +49%;解码 +5–15%(部分与上游重叠) | issue #149 | 用户实测 |
| 五行正确性修复 | 五处 file:line(见第八章边栏) | issue #149 | 用户实测 |
| 三卡车道 | 引擎自报 78.8 / 78.4 / 80.1,合 237.3(非同基准) | issue #127 | 用户实测 |
| 版本回退疑云 | 0.1.13 73.1/76.2 对 0.1.21 73.8/74.3,无回退 | issue #119 | 用户实测 |
| 低端 12 GB 卡 | RTX 3060 12GB 约 30 tok/s(降压降频) | issue #119 评论 | 用户实测 |
| agent 循环重读 | 150K–168K 提示每轮重读;命中率均值约 82.4% | issue #143 | 用户实测 |
「级」这一列:一手=仓库内文件或论文原文,可以直接翻到;作者自测=作者在他那台机器上量的,方法在仓库里;用户实测=issue 里的自述,方法各异;估算=作者按带宽与显存推算并自标误差。
Strata 值得看的地方不在于它跑得多快,而在于两件事同时做到了什么程度:它把「为这一台机器、这一个模型重写运行时」推到了多细,以及它把全部工作痕迹公开到了什么程度——12 组带日期的基准、试错失败的记录、99 个 issue 里用户报上来的实测。