第二次调优结果放在#16
先说结论,再讲过程。
这台机器现在稳定跑 54.0 tok/s(decode)、571 tok/s(prefill),支持 128K 上下文,使用 IQ3_S 量化,OpenAI + Anthropic 兼容接口常驻 8080。
显卡、内存、主板三件合计 2700 元:MI50 16G 每块 750 元,32G DDR3 REG ECC 4 条共 900 元,华南金牌 X99-TF 二手 300 元。
折算下来:32GB HBM2 显存折合 46.9 元/GB,128GB DDR3 内存折合 7.0 元/GB。同样运行这个模型,一张 4080 SUPER 16G 大约能给到 58 tok/s,但仅那张卡的价格就足够配三台这样的机器。
一、硬件与成本
| 项目 | 型号 / 规格 | 价格 |
|---|---|---|
| 显卡 | AMD Instinct MI50 16GB ×2(已改主动散热) | 750 × 2 = 1500 |
| 内存 | DDR3 1333 RDIMM 32GB ×4(共 128GB) | 900 |
| 主板 | 华南金牌 X99-TF(二手) | 300 |
| CPU | Intel Xeon E5-2696 v3(18 核 36 线程) | 未计入 |
| 硬盘 | 1TB NVMe(系统 + 模型) | 未计入 |
| 电源 | 长城 1250W 金牌 | 未计入 |
| 已计合计 | 显存 32GB + 内存 128GB | 2700 |
有几点值得单独说明:
- MI50 是 HBM2,单卡 1024 GB/s。这不仅是“便宜的显存”,它的显存速度还超过很多家用卡。双卡合计 2TB/s 的名义带宽,是整个方案里最不合常理的性价比来源。
- DDR3 1333 四通道实测约 30 GB/s。这是这台机器真正的短板,后面所有调优都围绕它展开。
- 双卡各需 2×8pin,共 4 根;1250W 金牌电源完全过剩(没有做整机功耗实测,这里不编造数字)。
- BIOS 必须开启
Above 4G Decoding,否则卡无法启动。 - MI50 是涡轮或被动散热的服务器卡,必须改散热(我这套已改主动散热)。
硬件验收的原始数据(lspci / sysfs 实测):
- 两卡均为
Vega 20 [1002:66a1],mem_info_vram_total各 16.0 GiB(确认是 16G 版,不是 32G 版); LnkCap/LnkSta两卡都是Speed 16GT/s, Width x16,Gen3 x16 满血,无需争抢槽位;/dev/kfd、/dev/dri/card1、card2、renderD128、renderD129齐全。
二、这套卡为什么需要“自己编译”
gfx906 在 Strata 里是社区验证路径,官方的“支持的硬件”表里根本没有 MI50。这里有三条硬性约束:
- 官方 Setup 不会自动构建 gfx906,必须从源码自行构建,编译参数中带上
-DSTRATA_HIP_GFX906=ON(注意不是STRATA_ENABLE_HIP,两者互斥,同时给出会直接触发 FATAL_ERROR)。 - ROCm 官方版已经不再包含 gfx906 的库,必须使用社区镜像
mixa3607/rocm-gfx906:7.14-complete,并走内核自带的 amdgpu 驱动,不使用 hipBLASLt。 - 宿主机不需要安装 ROCm,只需要内核 amdgpu、
/dev/kfd和 Docker,所有工具链都在容器里。
编译命令(在容器内执行,源码位于 /src/Strata):
cmake -S . -B build-906 -G Ninja \
-DSTRATA_HIP_GFX906=ON \
-DCMAKE_HIP_ARCHITECTURES=gfx906 \
-DSTRATA_GGML_DIR=/src/llama.cpp \
-DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang \
-DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ \
-DCMAKE_BUILD_TYPE=Release
ninja -C build-906 strata
165/165 全部通过,36 线程约 3 分钟。 产出的二进制文件为 40MB;若在宿主机上直接运行,会报 libhipblas.so.3: cannot open shared object file,所以所有命令都必须在容器内通过 docker exec 执行,以免踩到这个坑。
编译期唯一的坑是:include/strata/platform/hip_compat/strata_hip.h 缺少两个映射宏。原因是 gfx906 构建不定义 STRATA_USE_HIP,dflash2.cpp 会走 CUDA 兼容分支,从而报 use of undeclared identifier。补上两行即可(上游有 PR #3986,但当时还没合入 0.2.11):
#define cudaStreamCreateWithPriority hipStreamCreateWithPriority
#define cudaDeviceGetStreamPriorityRange hipDeviceGetStreamPriorityRange
三、模型:从 IQ3_XXS 换到 IQ3_S,体现了反直觉的正确决定
模型是 ISTA-DASLab/Qwen3.8-Flash-Next-GSQ-RCO-GGUF。它的体积口径很容易被误读:47.0GB 只是权重,实际下载量为 47.04 + 28.80 = 75.84GB——第二个分片是 28.8GB 的 per-token 表。
下载走 hf-mirror.com,峰值 48.8 MB/s,75.84GB 用时 19 分 08 秒,下载完成后按精确字节数校验(这类数字必须一个字节不差)。
构建 pack(使用 tools/iq_pack.py,注意必须加 --experts-bin,否则 mmap 模式无法启动):
| 量化 | experts.bin | dense.bin | top-1 一致率 | 专家槽位 | decode |
|---|---|---|---|---|---|
| IQ3_XXS | 42.9 GB | 1.43 GiB | 84.46% | 15,123 | 37.5 |
| IQ3_S | 50.3 GB | 1.54 GiB | 87.69% | 12,741 | 37.7 |
我原本的判断是“专家块大 16.6%,显存槽位少 16%,decode 肯定会掉 8%”——实测没有下降,几乎持平。原因大概是命中率对槽位数并不敏感。因此在这台机器上换成更高精度档相当于免费获得质量提升,我把生产线直接换成了 IQ3_S。
另外,IQ3_S 和 IQ3_XXS 的第二个分片(那 28.8GB 的表)逐字节相同,用软链接复用即可,省下 29GB 下载量。
四、调优全过程:六个台阶,从 28.3 到 54.0
每个数字都是先重启服务、空转 90 秒、用 6 条短请求预热之后才测得的(这点非常重要,见第五节)。全程使用同一台机器、同一份模型文件。
| 阶段 | 改动 | decode |
|---|---|---|
| 首跑 | IQ3_XXS,出厂参数 | 28.3 |
| ① | --spec 2 + --pool-workers 32 |
31.0 |
| ② | 解除 --adapt-every 0 |
37.6 |
| ③ | 换 IQ3_S | 37.7 |
| ④ | 升级 0.2.11 + DFlash2 草稿模型 | 49.3 |
| ⑤ | MTP 草稿 + --pipeline-windows 2 + --trim-stage-weights |
54.0 |
最后一轮使用官方口径(1.6K prompt / 128 token / temp 0)复核过的五组对照:
| 组合 | decode | prefill |
|---|---|---|
| 官方原参数(无草稿层) | 34.8 | — |
+ --pipeline-windows 2(该组合下开关被静默关闭,见第五节) |
35.9 | — |
| + MTP 草稿 | 47.1 | — |
| + MTP + 流水线 + trim(IQ3_XXS) | 51.8 | — |
| + MTP + 流水线 + trim(IQ3_S) | 54.0 | 571 |
| 同机 IQ3_S + DFlash2 | 48.3 | 571 |
| 官方参考机(2×MI50 16G,0.1.43.1,IQ3_XXS) | 49.2 | 353 |
比官方参考机快 10%,prefill 高 62%,而且量化档更高。
关键参数(生产配置):
{
"exe": "/src/Strata/build-906/strata",
"args": [
"--pack", "/src/models/packs/iq3s",
"--native", ".../IQ3_S-00001-of-00002.gguf",
"--ple-gguf", ".../IQ3_S-00002-of-00002.gguf",
"--expert-profile", "/src/models/learned-iq3s.bin",
"--expert-profile-save", "/src/models/learned-iq3s-mtp.bin",
"--expert-profile-save-every", "3",
"--expert-cache", "auto",
"--prefill", "auto",
"--spec", "3",
"--max-context", "131072",
"--kv", "int8", "--kv-resident", "32768",
"--layer-split", "27", "--trim-stage-weights",
"--mtp", "/src/models/mtp/rt",
"--pipeline-windows", "2",
"--pool-workers", "32",
"--pcie-frac", "0", "--vram-reserve-mib", "1024"
],
"env": { "STRATA_RESIDENT_HEADROOM_GIB": "6" }
}
五、三个“静默失效”的坑,比参数本身更重要
这三个坑的共同点是:服务仍然让 /health 返回 ok,也仍然正常输出文字,但你调的东西根本没有生效。
1. --adapt-every 0 会静默关闭自适应专家缓存层
官方冒烟命令里带着 --adapt-every 0,我早期配置直接继承了它。后果是:
- 专家缓存退化成出厂 profile 的静态 15,123 个槽位,永远不会学习——同一批请求连续测三遍,命中率分别为 0.709 / 0.711 / 0.711,一个数都没变;
--expert-profile-save根本不会落盘。
引擎原话:
strata serve: --expert-profile-save needs the adaptive tier
(--adapt-every and --adapt-swaps above 0) and --expert-profile: nothing will be saved
去掉这个参数后,命中率直接从 0.71 跳到 0.98,decode 提升 27%(从 29.7 到 37.6)。这是整台机器上成本最低的一次提速,代价只是删掉四个字符。
2. --pipeline-windows 2 必须配合 MTP,否则会被静默禁用
我第一次测试流水线时,前后两轮都“没有收益”,于是得出了“流水线无用”的结论。后来补加 --mtp 才发现日志里一直写着:
strata generate: --pipeline-windows 2 is off: it needs the MTP drafter (--mtp, --spec)
这个开关只作用于 MTP 草稿路径。 它生效时的自检行是下面这三条:
strata mtp: draft layer loaded, 837 MiB of VRAM (experts 675, dense 111)
--pipeline-windows 2: 290 MiB of CUDA0 kept out of the expert cache
--pipeline-windows 2: two verifiers per stage, 2 stages overlap across windows (decode too)
3. DFlash2 和流水线不能共存,这是源码里写死的
DFlash2 从“已经跑完的那个窗口的 hidden taps”里起草,而流水线的本质是让两个窗口在时间上重叠,两者的数据依赖直接冲突。源码 generate.cpp:7103 的拒绝理由如下:
it does not run beside --pipeline-windows (the MTP's speculative next
window has no DFlash2 counterpart); --pipeline-windows 0
所以只能二选一:DFlash2(48.3)或 MTP + 流水线(54.0)。顺带说,--draft auto(两个草稿按请求二选一)也不适用于双卡 layer split,源码写明它是给“单卡、单请求”使用的。
六、测速方法学:否则测出来的结果并不可靠
这点我和公开的 fieldnotes 结论完全一致,值得单独说明:
- 重启后必须空转 ≥90 秒,再发 6 条预热请求,然后才能开始计分。 冷启动后首次测量的数字会偏低 30–40%,而且不同配置的冷启动偏差不同——如果拿第 1 个样本做 A/B 对比,结论会非常自信却完全错误。
- 重启必须同时杀掉
serve.server和引擎子进程,并且要在容器内执行 pkill。 如果只杀引擎,serve.server会立刻把它重新拉起来并继续占用 8080,新实例会静默失败,/health会立刻返回旧服务的响应——我第一轮就是这样把上一版的数字当成了这一版的。 - 启动后核对
/v1/models的模型名是否等于本配置的model_name,如果不一致,直接中止测量。 - 权威数字取
/metrics中服务端自报的行,不要用客户端 wall time 估算。 - 修改配置时删除参数,要连它的值一起删除,否则残留值会变成位置参数,报
unknown argument: 2,而 serve 只会说“引擎启动前退出”。
七、128K 上下文和内存的实际分配方式
| 位置 | 内容 | 实测 |
|---|---|---|
| 显存(两卡) | 专家缓存 + dense 权重 + 草稿层 + KV 窗口 | 每卡约 15.9 / 16.0 GiB,余量 1.1 GiB |
| 系统内存 | PLE 表 26.8 GiB(锁页) | --ple-io ram 自动选中 |
| 系统内存 | 专家常驻 arena / 文件页缓存 | 50.3 GB 的 experts.bin |
| 系统内存 | KV 后备 | --kv-resident 32768:每 QSA 层 32768/131072 个 cell 在显存,其余 K/V 落在锁页内存(早期设 20480 时实测锁页 K/V 为 0.77 GiB) |
要点是:128K 上下文不等于把 128K KV 全部塞进显存。 这个模型 48 层里只有 12 层 QSA 需要 KV,其余 36 层是 GDN 线性注意力,KV 成本天然就小;再配合 --kv int8 和 --kv-resident 32768 的流式方案,就能实现。
实测长 prompt 表现:
- 61,859 token 冷预填耗时 92.8 秒 ≈ 666 tok/s;
- 15.6K token 预填约 29 秒;
- 64–69K token 的长会话因为复用了 conversation cache,decode 达到 33.5–54.8 tok/s、命中率 0.78–0.93。
冷启动到 /health ok:84 秒(首次锁页的那几分钟会很安静,别以为程序挂了)。
八、横向对比
| 平台 | 卡 | decode | 备注 |
|---|---|---|---|
| 本机 | 2×MI50 16G | 54.0 | IQ3_S + MTP + 流水线 |
| 官方参考机 | 2×MI50 16G | 49.2 | IQ3_XXS,Ryzen 7 5700X + DDR4-3600 |
| 社区同卡 | 2×Radeon Pro VII 16G | 47.8 | IQ3_S,Ryzen 5 2600 + DDR4-2133 |
| 单卡 4080 SUPER | 1×16G | 58.8 | IQ3_XXS,i7-12700 + DDR4-3200 |
| 同机 llama.cpp | 2×MI50 16G | 26.3–26.6 | Qwen3.8-27B UD-Q4_K_M,pp 228.5 |
两点说明:一是同一张卡搭配不同 CPU,差距非常明显,我测得 54.0,而公开数据是 47.8,差异主要来自 MTP + 流水线这套组合,并不完全由硬件决定;二是 27B dense 在这套卡上只有 26 tok/s 量级,而 125B 总参 / 6B 激活的 MoE 反而更快——这台机器注定应该跑 MoE。
九、性价比换算
| 口径 | 数字 |
|---|---|
| 显卡/内存/主板合计 | 2700 元 |
| 显存成本 | 46.9 元 / GB(HBM2) |
| 内存成本 | 7.0 元 / GB |
| 每 1000 元买到的 decode | 20 tok/s |
| 对比:4080 SUPER 16G | 单卡价格约 3–4 倍,decode 只高 9% |
再补充两点:MI50 单卡标称 300W TDP,但轻载推理时的实际功耗并不高,1250W 电源纯属过剩;这台机器同时还能当 128GB 内存的普通服务器使用(我平时在上面跑 Docker 做杂活)。
一句话概括:用显存买速度,用内存买容量,用老 CPU 硬扛剩下的部分——这条路能走通,前提是你愿意花一晚上编译。
十、未验证 / 待办事项
这里如实标注,避免误导:
- 质量还没有做正式评测。 目前只跑了冒烟测试和讲义风格的中文长文样本(情境、公式、易错点齐全,没有套话)。3bit 在中文长文上的典型失效是重复、断段、术语漂移,这需要真材料 A/B 对比才算数。
- 没有测试满 128K 的检索质量,最长实测输入为 61.9K。
- 没有测试并发。官方和社区都说这条路径并发不行,我也没有尝试。
- 没有做整机功耗和温度实测,散热是改过的,长期稳定性只观察了一天。
- 0.1.57 的
--draft mtp能否也配合流水线,还没有验证——我现在使用 0.1.55 是为了稳妥;DFlash2 那条线保留在/src/Strata-0211,随时可以切换。 - 层切分只试了 27/21(按官方给出的值),没有穷举,不要当成全局最优。
十一、一句话总结
750 元一块的 16G HBM2 卡两张、900 元的 128G DDR3、300 元的二手主板,总计 2700 元,达到 54 tok/s,支持 128K 上下文,兼容 OpenAI/Anthropic 双接口。
真正花时间的不是攒机,而是搞清楚哪些开关是真正生效的:--adapt-every 0 会让性能损失 27%,--pipeline-windows 2 在没有 MTP 时不会生效,DFlash2 和流水线在源码级就是互斥的。这三个坑的信息量,比任何一个调参数字都大。


