sskj/docs/Kimi-K3_PD-DFlash_16K512_C8性能归因.md
2026-09-02 11:16:53 +08:00

230 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/BNsight/PyTorch Trace 只用于归因。
## 3. 严格 A/B 配置
| 项目 | P 组 | D 组 |
|---|---|---|
| 节点 | 601-604 | 605-608 |
| 并行 | TP4 / PP8 / EP4 | TP32 / PP1 / EP4 |
| MoE | FlashInfer MXFP4A2A none | FlashInfer MXFP4A2A none |
| Chunk | 8192 | 8192 |
| KV Cache | BF16 | BF16 |
| 请求上限 / CUDA Graph BS | 8 / 8 | 8 / 8 |
| 显存比例 | 0.84 | 0.86 |
| 传输 | Mooncakemlx5_0/1/2/3 | Mooncakemlx5_0/1/2/3 |
请求固定为同一批 SPEED-Bench 数据40 请求C=8输出 512实际输入共 666,343 tokenwarmup=1seed=42temperature=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 stagePP7/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/
```