适用读者:要决定"专家放 GPU 还是 CPU"、要调专家并行度、要设计多级 KV 缓存与请求路由的人。
前置:S23-J18、J19(EP 与集合通信)、J10(路由与负载均衡)。
覆盖旧文:A-4342 §9.3–§9.5(AF 分离与异构执行 + 专家放置复制均衡 + KV 分布共享路由,449 点)→ 449 点。
扩展点:E-S23-090(每层 128 专家 4.5 GiB:驻留 32 → 106 GiB,装不下 74.5 GiB)、E-S23-091(E[U] = E[1−(1−K/E)^M]:64 token → 约 126 个专家)、E-S23-092(专家分离四步:注意力 → dispatch → 专家 → combine)、E-S23-093(KV 多级存储与路由:命中率由缓存标识决定)。
上一篇:S23-J24 · 分布式推理:布局与 PD 分离
下一篇:S23-J26 · 服务启动、扩缩容与部署比较。
1. 一句话结论
"每个 token 选 8 个专家"不等于"一批只碰 8 个专家"。 64 个 token 随机路由,平均会碰到 约 126 个专家,权重读取 4.43 GiB——比理想集中情形(288 MiB)多 16 倍。专家放哪,取决于"每专家几行输入"。
出处:AI Infra 教材 §9.3–§9.5(
id 4342),算例 Qwen3-235B-A22B。
活跃专家数期望:E[U] = E·[1 − (1 − K/E)^M]
权重复用 = 分派次数 / 活跃专家数
2. 容量:专家放不下就该分开(E-S23-090)
Qwen3-235B-A22B 每层 128 个 BF16 专家 = 4.5 GiB(94 层)。
| 每层驻留专家数 | 单层权重 | 94 层合计 | 能否放进 74.5 GiB |
|---|---|---|---|
| 32 | 1.125 GiB | 约 106 GiB | ❌ |
| 16 | — | 约 53 GiB | ✅(但更多专家走主存) |
一张 A100 的 80 GB ≈ 74.5 GiB 全部给专家也不够
判据:容量选择直接改变执行路径——驻留越少,越多 token 要等主存/CPU 侧算完,延迟变差。
3. 活跃专家数:随机路由的真实代价(E-S23-091)
M 个 token、每 token 独立均匀选 K 个不同专家(共 E 个)
E[U] = E·[1 − (1 − K/E)^M]
E=128, K=8, M=64 → 期望 约 126 个专家。
| 路由形态 | 活跃专家 | 每专家行数 | 权重读取(每份读一次) |
|---|---|---|---|
| 均匀覆盖 | 128 | 4 | 4.5 GiB |
| 随机(本算例) | 约 126 | 4 | 约 4.43 GiB |
| 完全集中 | 8 | 64 | 288 MiB |
有效计算量相同:512 次分派 × 2 × 18,874,368 ≈ 19.3 GFLOPs
主存 220 GB/s 下:22.0 ms vs 1.37 ms —— 差一个数量级
判据:batch 越大,活跃专家数越接近上限 E,但分派次数仍线性增长 → 新增 token 越来越多地复用已读入的权重。这就是"加 batch 能救专家读取"的原因(J13 的算术强度)。
3.1 什么时候该把专家放主存
| 条件 | 倾向 |
|---|---|
| 每专家行数少(batch 小、路由散) | 大主存 + 少 GPU(权重读取摊不薄,GPU 算力也用不上) |
| 每专家行数多(batch 大、路由集中) | 放 GPU(矩阵计算成为主导) |
公开配置(待核版本细节):DeepSeek V4-Flash 单张 RTX 5090 配置要求至少 200 GB 主存;Kimi K2 的 Q4_K_M 分组量化配置约需 600 GB 主存。
4. 专家分离:把分界切到层内(E-S23-092)
两台 HGX H100:A0–A3 做注意力(持 KV),B0–B3 各持 1/4 路由专家。一层内四步:
① A 卡算完注意力 → 路由器为每 token 选 8 个专家
② dispatch:按目标卡分组,A→B 四对四 = 一次 All-to-All
③ B 卡按专家重排输入行 → 执行专家矩阵乘
④ combine:输出沿原路回 A,A 收齐 8 份按路由权重加权求和 → 进入下一层
判据:专家分离把"每层一次 All-to-All"变成常态。 与 PD 分离(一次 GB 级交接)不同,这是高频小消息——启动开销主导(J14 的 α + V/R),要靠 micro-batch 流水把 dispatch/combine 与注意力计算重叠。
4.1 专家副本与 skew(§9.4)
| 问题 | 现象 | 手段 |
|---|---|---|
| 计算 skew | 热点专家排队,冷专家空闲 | 专家副本、动态迁移 |
| 通信 skew | 某些 B 卡收到更多 token | 重排 dispatch、按负载选卡 |
| 副本开销 | 副本也占显存 | 按热度给副本数 |
判据:副本解决"热点",不解决"总量"——与 J19 的内存池同理。
5. KV 的分布、共享与路由(§9.5)
5.1 三种 KV 状态(E-S23-093)
| 层级 | 存放 | 作用 |
|---|---|---|
| GPU HBM | 活跃请求的块 | 最快、最贵 |
| 主机内存/远端 | 换出/共享池 | 容量大、延迟高(J19 的 α+V/R) |
| 持久化(checkpoint) | 磁盘/对象存储 | 跨会话复用,需部分重算校验 |
5.2 缓存标识决定命中
缓存标识 = 模型版本 + 分词器 + 前缀内容哈希 + 采样/位置参数
| 标识不一致 | 后果 |
|---|---|
| 模型/权重版本变了 | 旧 KV 不可用(数值不同) |
| 前缀只差开头几个 token | 整段命中失效(前缀必须完全一致) |
| 位置编码/压缩方式不同 | 不能混用 |
判据:"前缀缓存"命中率是工程指标,不是配置项。 要让 system prompt、工具描述、文档片段逐字节稳定,命中率才高。
5.3 请求路由
| 策略 | 适用 | 代价 |
|---|---|---|
| 轮询 | 无共享前缀 | 命中率低 |
| 前缀亲和(按 hash 路由) | 多轮对话、RAG 固定文档 | 负载可能不均 |
| KV 亲和(按已有 KV 所在节点) | 长上下文续写 | 需全局 KV 目录 |
| 负载感知 + 亲和加权 | 生产默认 | 实现复杂 |
6. 可复算(实测)
$ cd /root/博客运营官/blog-v2 && python3 tools/s23_book_calc.py | tail -6
专家容量: 每层 128 专家 4.5 GiB
驻留 32: 1.125 GiB/层 × 94 = 106 GiB(> 74.5 GiB 放不下)
驻留 16: 0.562 GiB/层 × 94 = 53 GiB(可放)
活跃专家(E=128,K=8,M=64): E[U] ≈ 126,理想权重读取 4.43 GiB
均匀 128 → 4.5 GiB;集中 8 → 288 MiB(16×)
计算量相同 19.3 GFLOPs;主存 220 GB/s:22.0 vs 1.37 ms
7. 前提与边界
| 结论 | 成立条件 | 不成立时 |
|---|---|---|
E[U] 公式 |
token 独立、均匀选 K 个不同专家 | 有热点/亲和 → 实际更集中 |
| 权重读一次 | 批内共享、无副本重复 | 专家副本 → 读取增加 |
| 专家放主存 | 每专家行数少 | batch 大 → CPU 算力先爆 |
| 前缀命中 | 前缀逐字节一致 | 差一个 token → 整段失效 |
8. 常见误区
| 误区 | 纠正 |
|---|---|
| "每 token 8 专家 → 一批只碰 8 个" | 随机路由平均约 126 个 |
| "专家越多越省显存" | 驻留少 → 更多走主存,延迟变差 |
| "专家分离没有通信" | 每层一次 All-to-All,高频小消息 |
| "专家副本能扩容量" | 只均摊热点 |
| "开前缀缓存就能命中" | 标识不一致(模型/前缀/位置)就失效 |
9. 自测
- 每层 128 专家、BF16 共 4.5 GiB,驻留 32 个时 94 层要多少?能放进 74.5 GiB 吗?
E=128, K=8, M=64,期望活跃专家数多少?权重读取约多少?- 专家分离一层内的四步是什么?哪一步是 All-to-All?
- 前缀缓存命中要求哪些条件一致?
答案
4.5×32/128 = 1.125 GiB/层 × 94 ≈ 106 GiB;❌ 放不下。128×[1−(1−8/128)^64] ≈ 126;约 4.43 GiB。- 注意力 → dispatch(All-to-All) → 专家矩阵乘 → combine。
- 模型/权重版本、分词器、前缀内容、位置/压缩方式。
10. 下一篇
S23-J26 · 服务启动、扩缩容与部署比较 —— 冷启动慢在哪、扩缩容为什么有粒度、单机/多机/PD/专家分离四种部署的成本与风险对照。