好文集ChatGPT Plus¥119

把 96.5GB 的 GLM-5.3-Flash 塞进单张 4090 48GB,我们如何跑出 41.8 token/s?

文章目录

一个 96.5GB 体积的混合专家(MoE)模型,一张 48GB 显存的单显卡。

模型体积比显存大了一倍以上。在常规认知里,这种单卡异构推理要么直接 OOM(显存溢出),要么被迫把大半参数甩给内存,让文字以一秒一两个词的龟速往外爬。

但在过去几天里,我们在一台配备 魔改版 RTX 4090 48GB、AMD Ryzen 9 9950X、128GB 内存 的工作站上,通过底层的系统级排障与压榨,最终把 GLM-5.3-Flash 的短文本生成速度推到了稳定的 41.8 token/s。

每个 token 耗时仅约 24 毫秒,P99 稳定在 31 毫秒 左右。

更关键的是,我们拆解出了这 24.47 毫秒里的每一个时钟周期究竟花在了哪里。

这是一篇纯粹由真实数据堆出来的工程排障实录。

它记录了我们怎样从理所当然的优化假设出发,经历反直觉的暴跌打脸,最终摸索出这套系统最优谐振点的完整过程。


01. 账本与底牌:96.5GB 凭什么能单卡解码?

在讲优化前,必须先把软硬件账本算得清清楚楚。

模型与量化配方

我们运行的是智谱的 GLM-5.3-Flash(Maya-S v2 分片打包),原生上下文高达 100 万,测试上下文配置为 8,192:

  • 结构体量:共 46 层(前 3 层为稠密层,42 层为 MoE 专家层,加 1 个 MTP 草稿层)。每层拥有多达 288 个路由专家,每个 token 激活其中的 8 个专家 + 1 个共享专家。

  • 专家总数:42 层 × 288 = 12,096 个路由专家。每个 token 会被路由到 336 个专家,对应需要读取约 2.39 GB 的专家权重。

  • 量化并非粗暴的 INT4 或 AWQ:而是 GGUF 体系下的混合 i-quant(矩阵校准量化)。专家的 Gate/Up 采用极致压缩的 IQ2_XXS(约 2.06 bit/权重,占 52.32 GB);Down 投影根据层敏感度分别采用 IQ2_S 与 IQ3_XXS;稠密层和共享专家则用 Q6_K 保证精度。

  • 体积折算:12,096 个专家总重 86.0 GB,每个专家平均仅 7.11 MB。

硬件与内存拓扑

  • GPU:RTX 4090 48GB(实际可用显存 47.37 GB),工作在 PCIe 4.0 x16。

  • CPU:Ryzen 9 9950X(16 核心 32 线程,单 NUMA 节点,双 CCD 各带 32MB L3 缓存,全核睿频约 5.66 GHz)。

  • 内存:128GB DDR5(系统可见 123.5 GiB)。全部 12,096 个专家完整常驻在显存或锁页主机内存中,测试全程 SSD 读取为 0。

既然全部能装进内存,推理引擎(Maya)在每生成一个 token 时,就必须在毫秒内完成一场极限调度:

  • 显存池(VIP 专座):能放几千个专家?

  • PCIe 走廊:缺少的专家,经 PCIe 搬去 GPU 算有多快?(实测每个专家 0.285 ms,折算吞吐约 25.0 GB/s,跑满 PCIe 4.0 理论带宽的 78%)

  • CPU 值班管家:不搬运,直接让 9950X 在内存里算有多快?(14 线程实测每个专家 0.084 ms,折算访存吞吐约 85.0 GB/s)


02. 第一次打脸:工人从 16 个砍到 8 个,速度暴涨 42%

文章配图

最初分析时,CPU 计算专家消耗了肉眼可见的时间。

程序员的第一反应往往是粗暴的:

“9950X 有 16 个大核,拉满 16 线程火力全开,不就快了吗?”

然而,第一次实测便直接粉碎了常识:

把 CPU 线程从 16 个砍掉一半到 8 个,推理速度从 24.6 token/s 飙升至 35.0 token/s,暴涨 42.3%!

为什么“工人砍掉一半,产能反而井喷”?

答案藏在现代多核 CPU 的微架构与系统调度里:

  1. 多任务通道冲突:Maya 引擎本身需要独立线程管理 PCIe 异步搬运和事件监听。当计算线程开满 16 个核心(在 taskset 0-15 限制下),计算线程与搬运线程疯狂互斥,产生严重的抢核等待。

  2. 双 CCD 缓存与内存带宽争抢:9950X 拥有两个 CCD,各享 32MB L3。当 16 线程同时高强度扫内存读专家权重时,内存控制器通道被彻底打碎,L3 频繁失效。

我们曾做过一组极端测试:把计算线程强行绑在单 CCD(0–7 号核)上,速度直接暴跌 30.9%。

8 个线程恰好给搬运和全局调度留出了“绿色通道”。

在异构计算中,局部的无序狂奔只会导致全局交通瘫痪。

在这之后,我们尝试了数据预取、SIMD 融合等局部 CPU 指令级调优,但无一例外全部以微跌告终。

这明确告诉我们:CPU 的代码已经不是第一瓶颈,它的出场频率才是。


03. 空间换吞吐:显存池从 43GB 到 46GB 的质变

文章配图

既然别让 CPU 太忙,那就让显存装下更多常客。

一开始,引擎的显存池上限设为相对保守的 43GB。扣除稠密层(6.86 GB)、KV Cache、激活值等开销后,留给专家的空间只有 34.24 GB(5,166 个槽位,平均每层 123 个)。

我们决定逼近硬件物理极限,将上限一举提高到 46GB(保留 700MB 冗余缓冲):

  • 专家池扩大到 37.02 GB,槽位增加到 5,586 个(每层 133 个),净增 420 个席位。

这 420 个位置彻底逆转了局势:

关键监控指标43GB 显存池(基线)46GB 显存池(调优后)变化幅度
显存命中率79.1%81.4%+2.3%
每 Token 需 CPU 计算专家数47.8 个40.5 个-15.3%
每 Token 经 PCIe 拷贝专家数~23 个21.2 个-7.8%
端到端生成速度36.49 tok/s40.56 tok/s+11.1%

这就是非均匀分布系统下的杠杆效应:

命中率看似只涨了区区 2.3%,但因为砍掉的是最拖累系统延迟的“CPU 兜底任务”(直降 15.3%),直接带动速度冲破 40 token/s。

那么把显存池再顶到 46.8GB(5,712 槽位)呢?

实测速度几乎零变化。因为高频专家已经被悉数捕获,再扩充的槽位只能兜住极低频长尾,边际收益已经归零。


04. 致命的代偿陷阱:解密 CPU_PLAN

文章配图

既然 GPU 这么猛,完全不用 CPU、把所有非命中专家都搬去显卡算,能不能起飞?

在 Maya 引擎中,有一个核心参数叫 STRATA_GLM_CPU_PLAN。

它由一串 9 位数字构成,控制着每层 8 个被激活专家中,当有 k 个(k=0..8)落在内存时,究竟派几个给 CPU 算。

以 14 线程的最佳分工方案 011223344 为例:

  • k=1(只有 1 个缺席):交由 CPU 算 1 个,PCIe 搬运 0 个;

  • k=3(缺席 3 个):CPU 算 2 个,PCIe 搬运 1 个;

  • k=5(缺席 5 个):CPU 算 3 个,PCIe 搬运 2 个;

  • k=8(全部缺席):CPU 算 4 个,PCIe 搬运 4 个。

为了验证“全甩给 GPU”的想法,我们把 Plan 激进调整为偏向 PCIe 的 001122334(第一位变 0,意味着哪怕只有 1 个缺席,也必须通过 PCIe 搬到显卡):

结果惨遭滑铁卢:

  • 每 token CPU 的计算数从 40.5 砍到了 18.5(CPU 彻底摸鱼);

  • 但 PCIe 搬运专家数从 21.2 次激增到 36.4 次;

  • 最终速度从 40.56 崩塌到了 36.24 token/s!

真相大白:

PCIe 4.0 搬运一个专家需要 0.285 ms(实测折算 25 GB/s),而 CPU 本地算一个只需要 0.084 ms(访存折算 85 GB/s)。

盲目减轻 CPU 的负担,相当于逼着整个系统去挤狭窄的 PCIe 独木桥。

GPU 算力再强,也只能干等着流水线送粮。


05. 两个“看似聪明”的优化为什么落空?

调优过程中,我们还探索了两个技术上看似无懈可击、实测却双双扑空的策略:

  1. 晋升门槛惩罚:STRATA_GLM_PROMOTE_MIN
  • 假说:显存槽位珍贵,如果一个专家只是偶尔路过,就不该把它常驻,避免把别的有效专家挤掉。把晋升阈值从 0(默认见空位就进)改成 6(至少被点名 6 次才允许常驻)。

  • 实测:专家换入换出确实从每 token 20.5 次断崖式降至 5.9 次,但显存命中率从 81.4% 跌到 75.1%,速度降至 38.54 tok/s。

  • 原因:因为 128GB 大内存支撑下没有磁盘 I/O 惩罚,激进的准入限制反而让 GPU 显存陷入“大片空置无法利用”的窘境,CPU 负担不减反增。

  1. 按层非均匀专家槽位分配
  • 假说:不同 MoE 层的路由熵不同,深层和浅层活跃专家分布不一,按统计数据给不同层定制槽位容量。

  • 实测:命中率从 81.4% 变成 81.3%,生成速度毫无差异。

  • 原因:当前模型在 46GB 专家池下,每层 133 个槽位的均匀分布已经处于容错极高的甜点平原,人为引入的规则调度没有实质代际差。


06. 毫秒级解剖:一个 Token 耗时 24.47ms 的去向

文章配图

通过打桩开启 STRATA_GLM_PROF=16,我们在处理 1,159 个 token 的实际解码中,拿到了这台 4090 运行 GLM-5.3-Flash 最珍贵的耗时微观切片(最佳配置对比 43GB 基线):

GLM-5.3-Flash 逐 Token GPU 流耗时拆解 (合计 24.47 ms)
─────────────────────────────────────────────────────────────
[等 PCIe 搬运专家]  ██████████████████ 7.60 ms (31.1%) ★最大单项
[KDA 线性注意力]    █████████████ 5.38 ms (22.0%)
[GPU 算专家权重]    ████████ 3.31 ms (13.5%)
[MLA 稀疏注意力]    ███████ 2.85 ms (11.6%)
[路由/共享专家计算] █████ 2.14 ms (8.7%)
[GPU 等待 CPU 算完] ████ 1.75 ms (7.1%)
[HC 结构 / 稠密层]  ███ 1.44 ms (5.9%)
─────────────────────────────────────────────────────────────

这份剖析图彻底揭开了单卡大模型异构推理的物理本质:

  1. CPU 与 GPU 是重叠并行的:生成一个 token 时,CPU 在自己领走的 40.5 个专家上实际花了 8.55 ms。但因为绝大部分被 GPU 算力和 PCIe 传输给“掩盖”了,GPU 实际挂起等待 CPU(moe_cpu_wait)的时间只有 1.75 ms。

  2. 提速 1.39 ms 的真实源头:最佳配置(46GB/14 线程)比基线(43GB/8 线程)每 token 快了 1.39 ms。其中 1.18 ms 来自减少等待 CPU(显存装得更多了,CPU 算得更快了),另外 0.27 ms 来自减少等待 PCIe。

  3. 不可逾越的物理墙:注意力机制固定占死约 8.23 ms(34 层 KDA 线性注意力 5.38 ms + 12 层 MLA 2.85 ms)。而等 PCIe 搬运专家依然是系统最大的瓶颈(7.60 ms,占比 31.1%)。

只要显存无法一口气吞下 86GB 的全量专家,PCIe 传输时间就是这台机器的硬天花板。


07. 终局收敛:14 线程的稳态与 41.8 token/s

在搞定了 46GB 显存池与最佳 CPU_PLAN(011223344)之后,我们带着全新平衡态重新扫描了 CPU 线程数:

CPU 线程分配端到端生成速度运行表现分析
6 线程40.56 tok/s线程不足,CPU 掩盖能力下降
8 线程41.14 tok/s曾经的冠军点,依然稳健
10 线程41.56 tok/s吞吐稳步爬升
12 线程41.38 tok/s产生轻微时钟抖动
14 线程41.81 tok/s全场最佳谐振点(三次复测均值 41.74)
16 线程35.14 tok/s悬崖式崩塌(抢占主循环与搬运线程)

在 46GB 的全新环境里,系统最优点从 8 线程微移到了 14 线程。

因为显存留出余量、任务重新分流后,系统容许了更强悍的 CPU 并行计算来压缩那 1.75 ms 的纯等待耗时。

延迟稳定性与首字延迟(TTFT)

在最终的最佳配置下,我们在不同任务上验证了这套系统的稳定性(关闭思考模式,生成 256 token):

  • 生成波动极小:

  • CSV 解析任务:40.13 tok/s(P50: 24.85ms / P99: 31.59ms)

  • 物理原理解析:43.61 tok/s(P50: 22.69ms / P99: 29.69ms)

  • 网页画布生成:41.19 tok/s(P50: 23.96ms / P99: 31.50ms)

  • P99 仅为 P50 的 1.25~1.3 倍左右,解码体验极为平滑顺畅。

  • 首字延迟(TTFT)表现:

  • 24–30 token 短 Prompt:TTFT 为 0.67–0.85 秒(含约 0.65 秒的专家预热冷启动固定开销);

  • 1,000 token 长 Prompt:TTFT 为 1.65 秒(吞吐折算约 610 tok/s);

  • 4,000 token 长 Prompt:TTFT 为 2.74 秒(吞吐折算高达 1,460 tok/s)。


08. 最佳落地运行配置

为了方便社区复现与验证,附上本次跑出 41.8 token/s 最佳成绩的完整环境变量与启动命令:

CUDA_DEVICE_ORDER=PCI_BUS_ID CUDA_VISIBLE_DEVICES=0 \
STRATA_GLM_SPLIT=0 \
STRATA_GLM_VRAM_GB=46 \
STRATA_GLM_RESERVE_MB=700 \
STRATA_GLM_RAM_GB=64 \
STRATA_GLM_RAM_HEADROOM_GB=16 \
STRATA_GLM_CPU_LANE=14 \
STRATA_GLM_CPU_PLAN=011223344 \
STRATA_GLM_NO_MTP=1 \
taskset -c 0-15 ~/maya-v1016/build/strata \
  --serve \
  --glm-pack ~/models/GLM-5.3-Flash-Maya-S-v2-IQ2_XXS \
  --max-context 8192

09. 我们的观点:为什么 GLM 上不去,而 Qwen3.8-Flash-Next 能到 120 tok/s?

41.8 token/s 已经是这台机器跑 GLM-5.3-Flash 的稳态天花板。

嵌入推文(联网查看)

文章配图

把两个模型的架构摆在一起,答案其实很直白:

  • Qwen3.8-Flash-Next:48 层,总参数 125B + 51B n-gram,每 token 只激活 6B;每层 512 个路由专家,每 token 点亮 10 个路由专家 + 1 个共享专家;专家中间维度只有 640。

  • GLM-5.3-Flash:45 层(42 层 MoE),总参数 320B,每 token 激活 18B;每层只有 288 个路由专家,每 token 点亮 8 + 1 个;专家中间维度高达 2048。

我们的观点:GLM 的权重更大,但专家并没有更多。这就是它上不去的原因。

解码阶段是带宽瓶颈:每生成一个 token,都要把被激活专家的权重从显存或内存里读出来,读多少字节,大致等于“激活参数量 × 量化位宽”。

GLM 每个 token 要读 18B 的激活权重,Qwen 只需要读 6B,少读了三分之二。

120 ÷ 40 = 3.0,18B ÷ 6B = 3.0。速度比和激活参数比几乎一模一样。

再回头看这篇文章里的每一步:

  • GLM 的专家是粗粒度的:只有 288 个,每个专家又大又重(本文实测平均 7.11 MB)。每层 133 个显存槽位,只能装下不到一半的专家,剩下的必须靠 PCIe 搬运或 CPU 兜底。

  • 这就是第 04 节里 PCIe 独木桥的根源,也是第 06 节里 7.60 ms 等待搬运、1.75 ms 等待 CPU 的根源。

  • 我们把线程、显存池、CPU_PLAN 调到了甜点位置,但这些都只是在搬运和分工上做文章,没有改变“每个 token 必须读 18B 权重”这个前提。

Qwen 走的是另一条路:专家池更大、粒度更细,每个 token 只点亮一小撮。每 token 专家利用率只有 11/512 ≈ 2.1%,GLM 是 9/288 ≈ 3.1%。容量可以继续涨,而每 token 的读取量几乎不动。

所以,GLM-5.3-Flash 这条路,单卡异构的极限大概就在 40 tok/s 附近。

想要更快,靠的不是继续压榨调度,而是模型本身为极稀疏而设计的专家粒度。


结语:调优到底在调什么?

文章配图

把一个 96.5GB 的混合专家模型,在一张 48GB 显卡上从初期的卡顿一步步推向 41.8 token/s,给我们最深的体会是:

任何脱离全局数据流的“单点性能调优”,几乎都是盲人摸象。

  • 当你盯着 CPU 算力时,往往是线程争抢堵死了内存;

  • 当你全情投入 GPU 加速时,PCIe 的物理带宽可能正在发出无声的哀鸣;

  • 当你想用复杂的算法做精细控制时,均匀分配反而能给硬件提供最高的吞吐平原。

真正的系统工程压榨,不是把某一个指标做到极致,而是找准那个让 GPU 计算、PCIe 搬运与 CPU 分工严丝合缝、谁都不用停下来等待的动态平衡点。

全文完
查看原文

沿着这个主题继续读

从一篇文章,找到相关的教程、经验和阅读路径。

DeepSeek 与国产大模型DeepSeek、豆包、Kimi、Qwen、MiniMax 等国产大模型的实测、Agent 用法与本地部署。