两张64GB挖矿卡跑320B模型,384K下约90tok/s
背景
我在两张 64GB 的 CMP 170HX 显卡上折腾 GLM-5.3-Flash 有一阵子了,现在这套配置终于稳定下来,觉得可以拿出来分享。
我还拿它和同一台机器上一直在用的 Qwen3.8-Flash-Next 配置做了对比:vLLM + AWQ INT4 + FP8 PLE(PLE 是 Qwen3-Next 推理管线里的一个组件,这里保留原名)。
除了 PP/TG 基准测试(PP = Prefill,预填充;TG = Token Generation,生成),我还把两个模型都接上了 DSH(一个 Agent 任务执行框架),给它们同样的编码/Agent 小任务,看看原始推理速度到底如何转化为实际任务完成时间。
先说几个前提
* 这不是一次严格对等的量化对比(apples-to-apples)
* GLM 和 Qwen 用的是不同的推理引擎,以及不同的投机解码(speculative decoding,用一个小的草稿模型先提出候选 token、再由目标模型验证)配置
* 投机解码的速度高度依赖接受率(草稿 token 被目标模型采纳的比例)和实际生成的文本
* 编码任务只是几个实际例子,算不上严格的基准测试套件
* 下面提到的“Strata 风格”,指的是我之前用 Strata 时采用的 HBM-first/全驻留(full-residency)内存策略,并不是说这套配置跑的是 Strata 本身
仓库与可玩 Demo
GitHub:https://github.com/Flun/glm53-flash-cmp170hx-exl3
硬件环境
| CPU | Ryzen 5 5600X |
|---|---|
| RAM | 80GB DDR4 |
| GPU | 2x CMP 170HX 64GB |
| GPU 架构 | SM80 |
| PCIe | Gen2 x8 |
| GPU P2P | 不可用 |
| OS | Ubuntu 24.04 |
两个模型都在同一台机器上测试。Agent 测试用 DSH 作为执行框架。
(为什么这一步重要:CMP 170HX 是 64GB HBM2e 的挖矿卡改推理卡,PCIe 只有 Gen2 x8 且没有 P2P,带宽非常有限。后面选 HBM-first 而不是频繁搬运权重,就是被这个硬件约束逼出来的。)
GLM-5.3-Flash 配置
目标模型:turboderp/GLM-5.3-Flash-exl3,3.05bpw(bpw = bits per weight,每个权重的平均位数)
引擎:ExLlamaV3 1.5.4(简称 EXL3)
当前配置:
* GLM-5.3-Flash EXL3 3.05bpw
* 目标权重约 125.2GB / 116.6GiB
* 目标权重完全驻留在两张 64GB 显卡上
* k_hcfuse
* DFlash2 EXL3 6bpw(DFlash2 是投机解码用的草稿模型)
* DFlash2 K7
* Q8 KV 缓存(8-bit 量化的 KV 缓存)
* 实际使用 384K 上下文
* 最大请求预算约 392,960 tokens
GLM-5.3-Flash 本身是一个总参数 320B、激活参数约 18B 的 MoE(Mixture of Experts,混合专家)模型。
这里的关键是目标权重始终驻留在 HBM(High Bandwidth Memory,高带宽显存)里,解码过程中不会持续从系统内存流式加载专家权重。
关于 3.05bpw 的量化质量
这大概是我最关心的部分。
乍一看,3.05bpw 听起来是一个相当激进的量化,尤其是和人们常用的 UD Q4 系列(UD = Unsloth Dynamic,Unsloth 的动态量化格式)相比。但 EXL3 并不是简单地把每个张量都压成 3-bit。
它使用 trellis(网格)量化方案,根据张量的不同分配不同的位数。3.05bpw 只是一个平均目标比特率。
该模型家族发布的量化配置也会让更敏感的部分保持更高精度。例如在 3.05bpw 分支中,lm_head 仍然保留 6-bit。
看同一模型家族的 4.05bpw 版本,经过检查各部分的位数大致如下:
* 路由专家(routed experts):K4
* 注意力层(attention):K6
* 共享专家(shared experts):K6
* 稠密 MLP(dense MLP):K5
* lm_head:K6
* embedding / norms / router:原精度(native)
所以整体思路是:对体量庞大的路由专家部分压缩得更狠,而在更小、更敏感的通路上投入更多位数。
对于这种 MoE 模型来说很有道理,因为绝大部分存储都花在专家权重上。
与常见 UD 量化的粗略对比
| 量化格式 | 大小 | 与 BF16 的 Top-1 一致率 | 平均 KLD |
|---|---|---|---|
| UD-IQ3_XXS | 120.37GB | 81.63% | 0.28377 |
| EXL3 3.05bpw(我的目标) | 125.18GB | 约 93.05%(根据公开的 3.0bpw 结果估计) | 约 0.050(根据公开的 3.0bpw 结果估计) |
| UD-IQ4_XS | 156.82GB | 88.18% | 0.11665 |
| UD-Q4_K_XL | 199.71GB | 92.22% | 0.04929 |
| UD-Q5_K_XL | 240.31GB | 94.35% | 0.02705 |
作为参考,公开的 GLM-5.3-Flash GGUF(llama.cpp 生态的模型量化格式)保真度数据大致如上表所示。
有一点值得指出:名为 Q4_K_XL 的量化并不是字面上“整个模型每个参数 4 bit”。对于一个 320B 模型来说,约 200GB 其实是混合精度量化,有效平均比特率远高于 4bpw。
另外还有一个公开的 GLM-5.3-Flash EXL3 3.0bpw 保真度测试,使用 51,175 个留出的(held-out)next-token 位置,结果如下:
* Top-1 一致率:约 93.0%
* 平均 KLD(Kullback-Leibler Divergence,KL 散度,衡量分布差异):约 0.0505
从数值上看,这与公开的 UD-Q4_K_XL 结果大致处于同一水平。
不过,我不会仅凭这些数字就宣称“EXL3 3bpw 比 UD-Q4_K_XL 更好”。它们不是通过完全相同的评估流程/语料测出来的,而且公开的 3.0bpw 产物也不是我正在跑的同一份量化权重。
我的结论很简单:3bpw 这一档的 EXL3 在同样的体积下能保留惊人的保真度,表现并不像一个朴素的 3-bit 量化。
对我的使用场景来说,把目标模型压到约 116.6GiB、同时保留可用的编码/Agent 质量,正是这套配置有意思的主要原因。
DFlash2 6bpw 只是草稿模型
为了避免混淆:3.05bpw 的模型才是真正的 GLM 目标模型;6bpw 的 DFlash2 模型只是投机解码用的草稿模型(drafter)。
草稿模型先提出候选 token,GLM 目标模型负责验证。所以这绝不是“3.05bpw 和 6bpw 平均质量”之类的配置。草稿模型的量化主要影响草稿速度、显存占用和接受效率。
HBM-first / “Strata 风格”的内存策略
这部分借鉴了我之前用 Strata 时的思路。再次说明:这里跑的不是 Strata。
我说的“Strata 风格”其实很简单:尽可能让模型常驻 HBM,避免运行时 CPU↔GPU 之间的权重搬运。
GLM 目标模型本身可以装进两张卡,所以我让目标模型完全驻留。我没有选择把专家权重换出(offload),而是把注意力放在减少随上下文增长的那部分显存占用:KV 缓存和投机解码草稿模型。
对于这台机器来说,这比在 PCIe 上不断搬运权重要合理得多。
DFlash2 与 384K 上下文
一开始我用的是 GLM 自带的 MTP d2 路径(MTP = Multi-Token Prediction,多 token 预测)。后来换成了 DFlash2。
我没有保留原始的 BF16 incoai/GLM-5.3-Flash-DFlash2 草稿模型,而是把它转换成兼容 ExLlamaV3 的 EXL3 6bpw 版本,转换后的草稿权重约 0.96GiB。
长上下文场景下更大的问题其实是草稿模型的 KV 缓存。如果目标模型跑 384K,草稿模型也跟着长 384K 的 KV 缓存,显存很快就不够用了。
所以我把草稿模型一侧改成固定 SWA(Sliding Window Attention,滑动窗口注意力)窗口 + GPU 环形缓存(ring cache,固定大小的循环缓冲区)。目标模型仍然看到完整的 384K 上下文,并保留完整的目标 KV;只有草稿模型的 KV 存储被限制在一个有界环形缓冲区里。
这样当前配置(DFlash2 K7 + Q8 KV + 384K 目标上下文)才能跑起来,草稿缓存不会膨胀到完整目标长度。实现代码和验证测试都在仓库里。
冷启动时间
我还测了从冷启动编译/启动到 API 真正就绪的时间。
| 模型 | 就绪时间 |
|---|---|
| GLM-5.3-Flash EXL3 | 约 1 分 04 秒 |
| Qwen3.8 Flash Next / vLLM | 约 3 分 50 秒 |
这其实不算模型大小对比。Qwen 的 vLLM 配置启动时要做的事多不少:
* PP(Pipeline Parallelism,流水线并行)worker
* 分布式运行时
* 模型放置
* MTP
* PLE
* GDN
* Triton(GPU kernel 编译器)编译
* 内存分析
* KV 分配
而 ExLlamaV3 的 GLM 路径相对静态。
推理基准测试
以下数据来自我仪表盘上的实际负载。再说一次,尤其是投机解码,不要把数字当作普适的模型速度——接受率和生成文本本身影响很大。
GLM-5.3-Flash / DFlash2 K7 / Q8 实测数据
| 输入长度 | PP(预填充 tok/s) | Decode(生成 tok/s) |
|---|---|---|
| 8K | 1,529 | 95.6 |
| 40K | 1,624 | 90.7 |
| 73K | 1,647 | 90.6 |
| 106K | 1,650 | 91.0 |
| 131K | 1,611 | 94.4 |
| 385K | 1,535 | 90.1 |
在这个特定负载下,DFlash 的接受率大约在 87% 左右。
换成旧的 MTP d2 路径时,同样的负载一般在约 60 tok/s。改用 DFlash2 K7 之后,这里到了约 90 tok/s。
Qwen3.8 Flash Next / vLLM 实测数据
Qwen 的配置是:AWQ INT4(Activation-aware Weight Quantization,激活感知的 4-bit 权重量化)+ FP8 PLE / PP2(流水线并行度 2)/ MTP3。
| 输入长度 | PP(预填充 tok/s) | Decode(生成 tok/s) |
|---|---|---|
| 8K | 5,513 | 129.5 |
| 40K | 5,628 | 123.6 |
| 73K | 5,466 | 141.9 |
| 106K | 5,307 | 141.5 |
| 131K | 5,183 | 159.9 |
| 252K | 4,706 | 149.4 |
所以从原始吞吐量看,Qwen 明显更快。在约 131K 输入时:预填充约 5.18K vs 约 1.61K,解码约 160 vs 约 94 tok/s。这一点没什么可争的。
对我来说有趣的是,当我真正让两个模型去做多步编码工作时发生的事。
为什么暂时止步 384K
我测了约 385K 输入,预填充仍然有约 1.5K tok/s。问题不是预填充性能崩掉,而是墙钟时间(wall-clock time,实际流逝时间)。
从头预填充约 385K 已经要大约 4 分钟。就算我能塞下 1M,以这个速度做完整的 1M 冷预填充,日常使用也不太现实。所以目前我把服务保持在 384K Q8。
不过我还是想看看能不能跑通 1M,主要是为了技术上的挑战。
小型 Agent 任务测试
本来我只打算让两个模型都做一个俄罗斯方块就收手。两个模型都接上 DSH,收到完全相同的请求。
1. 俄罗斯方块
提示词:“构建一个可玩的网页版俄罗斯方块游戏。”
| 模型 | 完成时间 |
|---|---|
| GLM-5.3 | 4 分 40 秒 |
| Qwen3.8 | 5 分 30 秒 |
两个模型都给出了可运行版本,说实话差距不大,不够有意思。所以我又加了两个任务。
2. AI 迷你 PC 落地页
给两个模型的完全相同的提示词:“构建一个精美的单文件 HTML 落地页,面向 AI 迷你 PC,包含深色主题、规格、性能图表、定价、FAQ 和流畅滚动动画,不使用任何外部库。”
| 模型 | 完成时间 |
|---|---|
| GLM-5.3 | 10 分 05 秒 |
| Qwen3.8 | 14 分 40 秒 |
3. Vampire-Survivors 风格游戏
提示词:“构建一个单文件 HTML 的 Vampire-Survivors 风格游戏,包含 WASD 移动、自动攻击、敌人波次、经验值、三选一升级、血量、游戏结束和重新开始,不使用任何外部库。”
| 模型 | 完成时间 |
|---|---|
| GLM-5.3 | 4 分 40 秒 |
| Qwen3.8 | 24 分 10 秒 |
这个任务的差距比我预想的大得多。有一个明显的保留点:Qwen 版本加了音效,而 GLM 版本没有。提示词并没有要求音效,所以我也没有回头让 GLM 补上——我希望两次运行都保留在同一个一次性提示词(one-shot prompt)的结果上。
任务完成时间汇总
| 任务 | GLM-5.3 | Qwen3.8 |
|---|---|---|
| 俄罗斯方块 | 4 分 40 秒 | 5 分 30 秒 |
| 落地页 | 10 分 05 秒 | 14 分 40 秒 |
| Vampire 风格游戏 | 4 分 40 秒 | 24 分 10 秒 |
我不会从三个例子中过度解读,这绝不是“GLM 编码强 5 倍”之类的证据。
我觉得有趣的只是:原始 tok/s 和端到端 Agent 完成时间之间的相关性并不好。Qwen 的预填充和解码吞吐量都高得多,但在这些特定任务上 GLM 往往更早完成。
对于 Agent 任务来说,规划、重试次数、文件重读、编辑次数,以及第一版实现离能跑通有多近,都很重要。所以我认为原始推理速度和实际任务完成时间值得分开看。
当前配置总结
我现在在用的 GLM 服务是:
* GLM-5.3-Flash EXL3 3.05bpw
* DFlash2 EXL3 6bpw K7
* Q8 KV
* 384K 上下文
这个配置我最喜欢的地方是显存/质量的取舍。目标模型装进约 116.6GiB 的 HBM,保持驻留;公开的 3bpw 档 EXL3 保真度数据也表明,这个量化的表现比我单看比特率时的预期好得多。
运行层面本质上就是 HBM-first 思路:让目标权重驻留,然后在草稿模型/KV 一侧省内存,而不是在解码过程中来回搬运专家权重。
完整配置、转换脚本、环形缓存改动、基准测试代码和原始结果都在这里:
GitHub:https://github.com/Flun/glm53-flash-cmp170hx-exl3
如果有人在其他奇怪的 128GB 级 GPU 配置上跑 GLM-5.3-Flash,我也很想看看你们的数字。
接下来我想试的是 1M 上下文,不过到那个时候,预填充时间大概比“能不能塞下”更棘手。