230 lines
10 KiB
Markdown
230 lines
10 KiB
Markdown
# Kimi-K3 PD+DFlash 16K→512、C=8 性能归因
|
||
|
||
更新时间:2026-09-02
|
||
|
||
## 1. 目标与结论
|
||
|
||
本实验在 8 台 RTX 6000D 上,用同一镜像、同一 SPEED-Bench 请求和同一服务参数,对比:
|
||
|
||
- 标准 PD,无投机解码;
|
||
- PD+DFlash;
|
||
- 历史 PP8 双实例无投机结果,用作同数据集拓扑参考。
|
||
|
||
核心结论:**当前 PD+DFlash 同时放大了 P→D 交接成本和 D 侧单步成本,平均接受长度 3.69 不足以覆盖这些开销。瓶颈不是 400G Rail 被打满。**
|
||
|
||
与严格同口径 PD 无投机相比,PD+DFlash:
|
||
|
||
- P→D 每请求交接数据约从 `85.76 MB` 增至 `183.54 MB`,增加 `114.0%`;
|
||
- D 侧 transfer wait 窗口从 `15.24 s` 增至 `22.24 s`,增加 `7.00 s`;
|
||
- Mean TTFT 增加 `7.40 s`,其中 transfer wait 增量相当于 TTFT 增量的 `94.5%`;
|
||
- Mean TPOT 增加 `41.9%`,Input/Output TPS 均下降 `29.0%`;
|
||
- Nsight 中 `_fwd_kernel` 占 GPU kernel 时间 `49.8%`,NCCL AllReduce 占 `31.9%`,两者远高于 MoE/GEMM。
|
||
|
||
这里的 `transfer wait` 是 D 请求进入后等待 P 完成计算、bootstrap/注册并交付数据的整段窗口,不等于纯 RDMA wire time。
|
||
|
||
## 2. 指标口径
|
||
|
||
设第 `i` 个请求的发出、首 token、结束时刻为 `s_i/f_i/e_i`,输入和输出 token 数为 `I_i/O_i`,整轮 benchmark 墙钟时间为 `D`:
|
||
|
||
| 指标 | 计算方式 | PD 场景中的含义 |
|
||
|---|---|---|
|
||
| TTFT | `f_i - s_i` | Router、P 排队/Prefill、P→D 交接、D 接入及首 token 的总和 |
|
||
| TPOT | `(e_i - f_i) / (O_i - 1)` | 首 token 后平均每个输出 token 的耗时,主要由 D 侧决定 |
|
||
| E2E | `e_i - s_i` | 完整请求延迟 |
|
||
| Input TPS | `ΣI_i / D` | 整个系统墙钟吞吐,不是 P 的纯 Prefill 算力 |
|
||
| Output TPS | `ΣO_i / D` | 整个系统墙钟吞吐,不等于 `1 / Mean TPOT` |
|
||
|
||
DP、Router 和 PD 不改变公式,只改变时间花在哪个阶段。Profiler 本身开销很大,因此性能数字只取无 profiler 的严格 A/B;Nsight/PyTorch Trace 只用于归因。
|
||
|
||
## 3. 严格 A/B 配置
|
||
|
||
| 项目 | P 组 | D 组 |
|
||
|---|---|---|
|
||
| 节点 | 601-604 | 605-608 |
|
||
| 并行 | TP4 / PP8 / EP4 | TP32 / PP1 / EP4 |
|
||
| MoE | FlashInfer MXFP4,A2A none | FlashInfer MXFP4,A2A none |
|
||
| Chunk | 8192 | 8192 |
|
||
| KV Cache | BF16 | BF16 |
|
||
| 请求上限 / CUDA Graph BS | 8 / 8 | 8 / 8 |
|
||
| 显存比例 | 0.84 | 0.86 |
|
||
| 传输 | Mooncake,mlx5_0/1/2/3 | Mooncake,mlx5_0/1/2/3 |
|
||
|
||
请求固定为同一批 SPEED-Bench 数据:40 请求,C=8,输出 512,实际输入共 666,343 token,warmup=1,seed=42,temperature=0。DFlash 的 draft token 数为 16。
|
||
|
||
镜像:
|
||
|
||
```text
|
||
local/sglang:kimi-k3-pp-dflash-33863-fi0618-situ4460-kvbounds1
|
||
```
|
||
|
||
## 4. 端到端结果
|
||
|
||
| 指标 | PP8 双实例无投机(历史参考) | 严格 PD 无投机 | 严格 PD+DFlash | DFlash vs PD 无投机 |
|
||
|---|---:|---:|---:|---:|
|
||
| 成功请求 | 40 | 40 | 40 | - |
|
||
| Benchmark duration | 335.95 s | 255.29 s | 359.53 s | +40.8% |
|
||
| Input TPS | 1983.46 | **2610.18** | 1853.35 | -29.0% |
|
||
| Output TPS | 60.96 | **80.22** | 56.96 | -29.0% |
|
||
| Mean TTFT | **6.88 s** | 15.40 s | 22.80 s | +48.1% |
|
||
| Median TTFT | **7.04 s** | 16.03 s | 23.07 s | +43.9% |
|
||
| P95 TTFT | **10.64 s** | 22.18 s | 40.54 s | +82.8% |
|
||
| Mean TPOT | 116.13 ms | **63.22 ms** | 89.70 ms | +41.9% |
|
||
| P95 TPOT | 136.01 ms | **70.45 ms** | 144.76 ms | +105.5% |
|
||
| Mean E2E | 66.22 s | **47.70 s** | 68.63 s | +43.9% |
|
||
| DFlash 接受长度 | - | - | 3.69 | - |
|
||
|
||
PP8 双实例结果使用相同数据,但缺少本轮完整命令归档,只作为拓扑参考。严格单变量结论来自后两列。
|
||
|
||
## 5. TTFT 瀑布拆解
|
||
|
||
从 P 最后一个 PP stage(PP7/TP0)和 D TP0 日志中去重,排除 warmup 后取正式 40 请求:
|
||
|
||
| 阶段 | PD 无投机 | PD+DFlash | 变化 |
|
||
|---|---:|---:|---:|
|
||
| P bootstrap | 5.18 s | 6.67 s | +28.7% |
|
||
| P forward | 9.51 s | 14.22 s | +49.5% |
|
||
| P queue | 1.94 s | 0.93 s | -52.3% |
|
||
| P→D payload / 请求 | 85.76 MB | 183.54 MB | **+114.0%** |
|
||
| D bootstrap | 5.15 ms | 4.85 ms | 基本不变 |
|
||
| D allocation wait | 12.67 ms | 2.78 ms | 可忽略 |
|
||
| D queue | 0.41 ms | 0.30 ms | 可忽略 |
|
||
| D transfer wait | 15.24 s | 22.24 s | **+45.9%** |
|
||
| D forward | 32.35 s | 46.10 s | **+42.5%** |
|
||
|
||
### 5.1 为什么说 PD 交接冲散了收益
|
||
|
||
DFlash 需要向 D 节点交接额外的 draft/KDA 状态,使每请求 payload 增加约 `97.78 MB`。D 侧 transfer wait 随之增加 `6.998 s`,而 Mean TTFT 总共增加 `7.402 s`。两者不是可相加的独立阶段,但增量高度一致,说明 TTFT 回退主要发生在 P 计算与 P→D 交接链路,而不是 D admission 排队。
|
||
|
||
### 5.2 为什么不能把 22.24 秒叫作 RDMA 拷贝耗时
|
||
|
||
该时间戳从 D 开始等待传输到请求可进入 forward,覆盖:
|
||
|
||
```text
|
||
P 侧计算/准备 → Mooncake bootstrap 与 buffer 注册 → 数据传输 → D 侧完成可用
|
||
```
|
||
|
||
因此它是协议端到端等待窗口。纯线速传输 `183.54 MB` 即使只用一条 400G Rail,理论时间也只有毫秒级;当前几十秒来自依赖链、同步与大量小通信,而不是字节在线上连续搬运了几十秒。
|
||
|
||
## 6. RDMA 与 GPU 证据
|
||
|
||
严格 DFlash run 的 D 节点活跃 Rail:
|
||
|
||
- `mlx5_0` RX P95 约 `39.25-39.38 Gbit/s`;
|
||
- `mlx5_2` TX P95 约 `39.14-39.23 Gbit/s`;
|
||
- 峰值约 `42 Gbit/s`;
|
||
- 单 Rail 标称为 400G,当前峰值约为其 10%。
|
||
|
||
所以当前不是 raw bandwidth saturation。需要优化的是交接字节量、通信调用方式、同步依赖和通信域,而不是先增加 Rail 带宽。
|
||
|
||
严格 A/B 中 D 节点 GPU Util P95 均为 100%,DFlash D 节点平均利用率约 74%,并非 GPU 长期空闲等待网络;但高利用率不能区分有效计算、通信 kernel 和等待慢 rank,需结合 Nsight。
|
||
|
||
## 7. Nsight Systems 归因
|
||
|
||
Nsight 只包裹 D0 节点的 8 个本地 scheduler,保持 CUDA Graph 开启。Profile run 为 8 请求,仅用于时间线归因;其吞吐和延迟不能与无 profiler 结果比较。
|
||
|
||
### 7.1 全局 GPU kernel
|
||
|
||
| Kernel 类别 | GPU 时间 | 占比 | 调用数 |
|
||
|---|---:|---:|---:|
|
||
| `_fwd_kernel` | 14.59 s | **49.8%** | 4,590 |
|
||
| NCCL AllReduce Ring LL BF16 | 9.34 s | **31.9%** | 30,600 |
|
||
| MoE/GEMM 主 kernel | 约 1.0 s | 约 3.4% | - |
|
||
| NCCL AllGather | 0.53 s | 1.8% | - |
|
||
| KDA update | 0.42 s | 1.4% | - |
|
||
|
||
`_fwd_kernel` 通过 NVTX 相关性确定处于 DFlash target verify/draft forward 路径,但仅凭导出的 kernel 名还不能把它进一步等同为某个单一源码函数。
|
||
|
||
### 7.2 DFlash NVTX 阶段与 kernel
|
||
|
||
本轮额外标记:draft prepare、target verify、accept、ReplaySSM commit 和 draft KV materialize。
|
||
|
||
| NVTX 阶段 | 主要 GPU 内容 |
|
||
|---|---|
|
||
| Target verify | `_fwd_kernel` 51.0%;AllReduce 30.6%;MoE/GEMM 13.0%;AllGather 1.8% |
|
||
| Draft prepare/forward/sample | AllReduce 66.8%;`_fwd_kernel` 25.4%;MoE/GEMM 4.0%;AllGather 2.3% |
|
||
| Materialize draft KV | 主要为 GEMM/数据变换,GPU 总量较小 |
|
||
| ReplaySSM commit / accept | GPU 总量很小,不是主瓶颈 |
|
||
|
||
NVTX host range 平均每个 speculative cycle:
|
||
|
||
- target verify:约 18.07 ms;
|
||
- draft prepare/forward/sample:约 9.26 ms;
|
||
- materialize draft KV:约 1.44 ms;
|
||
- ReplaySSM commit:约 0.52 ms;
|
||
- accept:约 0.43 ms。
|
||
|
||
CUDA API 侧 `cudaEventSynchronize` 共约 `24.34 s`,占 CUDA API 时间 `82.9%`;`cudaGraphLaunch` 320 次、约 `2.89 s`。这说明 CUDA Graph 已生效,但每轮 speculative cycle 仍暴露出 target/draft/NCCL 依赖的同步等待。Graph 能减少 launch overhead,不能消除 collective 和状态依赖。
|
||
|
||
### 7.3 PyTorch Profiler 补充结果
|
||
|
||
PyTorch Profiler 已导出 P0 本地 PP0/PP1 的 8 份 trace,共约 `117 MiB`。但 P/D 的 `/stop_profile` 均返回 `Internal Server Error`,D trace 未落盘;profile 期间客户端流式 TTFT 也失真。因此本报告不从这轮生成新的性能结论,保留 P trace 供后续定位 Prefill operator stack。
|
||
|
||
实验入口已修正:P/D 的 `stop_profile` 改为并行请求并各自限时 180 秒,避免一侧导出异常让整轮实验永久挂起。
|
||
|
||
## 8. 两个问题的回答
|
||
|
||
### 8.1 PD 的收益为什么被传输冲散
|
||
|
||
不是单纯“网卡慢”,而是 DFlash 改变了 PD 协议要交接的内容:payload 翻倍、P 侧准备时间增加、D 侧必须等更多状态就绪。当前 D 侧 Rail 远未饱和,因此提升网卡带宽不会按比例降低 7 秒差额。
|
||
|
||
### 8.2 投机解码为什么收益很少,甚至变慢
|
||
|
||
平均接受长度 3.69 意味着一次 draft/verify 只推进约 3.69 个 token。每轮仍需:
|
||
|
||
```text
|
||
draft forward/sample
|
||
→ target verify
|
||
→ 大量 TP32 AllReduce
|
||
→ KDA/ReplaySSM 状态处理
|
||
→ accept 与 KV materialize
|
||
```
|
||
|
||
Nsight 显示 target verify 和 AllReduce 已占关键路径。接受长度不足以摊薄这些成本,最终 D forward 增加 42.5%,TPOT 增加 41.9%。
|
||
|
||
## 9. 后续优化顺序
|
||
|
||
1. **先减少 PD 交接内容**:确认哪些 draft/KDA buffer 必须由 P 传输,哪些可以由 D 本地重建或延迟生成;用 payload/request 和 TTFT waterfall 验收。
|
||
2. **降低 D 侧通信固定成本**:针对 TP32 小消息 AllReduce 比较更小通信域或 per-op Tree;必须保持同一 SPEED-Bench 复测正确性、TPOT 与接受长度。
|
||
3. **搜索 DFlash 的收益边界**:固定 PD 拓扑,扫 draft token 数和 tree 参数,目标是提高接受长度同时减少 verify 次数;不要只看接受长度,要看 `accepted tokens / speculative cycle cost`。
|
||
4. **最后再调 Mooncake/RDMA**:只有 HCA 接近线速或纯传输微基准异常时,才把 Rail 带宽作为主优化项。
|
||
|
||
## 10. 原始证据
|
||
|
||
严格 PD 无投机:
|
||
|
||
```text
|
||
/data/hzy/sskj/experiments/pro6000/kimi3_pro6000_pd_dflash_attribution/results/kimi3-pd-nospec-ab-pmem084-20260901-1808/
|
||
```
|
||
|
||
严格 PD+DFlash:
|
||
|
||
```text
|
||
/data/hzy/sskj/experiments/pro6000/kimi3_pro6000_pd_dflash_attribution/results/kimi3-pd-dflash-ab-pmem084-20260901-1823/
|
||
```
|
||
|
||
Nsight:
|
||
|
||
```text
|
||
/data/hzy/sskj/experiments/pro6000/kimi3_pro6000_pd_dflash_attribution/results/kimi3-pd-dflash-nsys-donly-20260902-100251/
|
||
/data/hzy/sskj/experiments/pro6000/kimi3_pro6000_pd_dflash_attribution/results/kimi3-pd-dflash-nsys-donly-20260902-100251/profiles/node_5/nsys/d0.nsys-rep
|
||
```
|
||
|
||
PyTorch Profiler(仅 P0 的 PP0/PP1 trace 有效):
|
||
|
||
```text
|
||
/data/hzy/kimi3_pd_dflash_attribution/kimi3-pd-dflash-torch-retry-20260902-103042/torch/
|
||
```
|
||
|
||
PP8 双实例历史参考:
|
||
|
||
```text
|
||
/data/cdflash_speedbench/matrix/bench_dual_16k_o512_c8.log
|
||
/data/cdflash_speedbench/matrix/bench_dual_16k_o512_c8.jsonl
|
||
```
|
||
|
||
本地归档:
|
||
|
||
```text
|
||
/Users/hzy/Desktop/infra/docs/evidence/kimi_k3_pd_dflash_attribution/
|
||
```
|