diff --git a/experiments/p800/P800_vs_H20_DIAGNOSIS_PLAN.md b/experiments/p800/P800_vs_H20_DIAGNOSIS_PLAN.md new file mode 100644 index 0000000..0f087cc --- /dev/null +++ b/experiments/p800/P800_vs_H20_DIAGNOSIS_PLAN.md @@ -0,0 +1,236 @@ +# P800 vs H20 性能差距根因诊断方案 + +## Context + +**已知差距**: DeepSeek-V4-Flash-INT8 模型在 TP8/DP1, ISL=4k, OSL=1k, C=16 下,P800 比 H20 慢 **~3x**(TPS: 1,634 vs 4,866)。 + +且 C=1 无竞争时 TPOT 差距仍有 2.9x(26.5ms vs 9.1ms),说明**瓶颈不在调度层,而在单次推理的硬件/算子层**。 + +**核心问题**: P800 纸面参数不弱(96GB/卡, 400W, 1450MHz, KL3 架构),但实际推理慢 3 倍。需要系统拆解这 3x 差距来自哪里。 + +--- + +## 分析框架 + +一次 Decode 推理的总时间 ≈ 三层叠加: + +| 层次 | 决定因素 | 测量方法 | +|:----|:---------|:---------| +| **计算层** | HBM 带宽、矩阵算力 (TFLOPS) | 内存带宽微基准测试、单算子基准 | +| **通信层** | XPULINK 带宽、All-reduce 效率 | BKCL 带宽测试、TP 扩展性实验 | +| **引擎层** | SGLang 框架开销、Kernel 调度效率 | 空跑开销分析、框架对比实验 | + +差距 3x = 带宽差距 × 通信开销差距 × 算子效率差距 + +--- + +## 实验方案 + +### 实验 0: 内存带宽实测(验证硬件理论值) + +这是最关键的第一步。P800 的 HBM 带宽没有公开文档,需要实际测出来。 + +**方法1: 用容器内的 PyTorch 测试** +```bash +# 在 SGLang 容器内运行 +python -c " +import torch +import time +x = torch.randn(1024, 4096, dtype=torch.float16).cuda() +y = torch.randn(4096, 4096, dtype=torch.float16).cuda() +# warmup +for _ in range(100): z = x @ y +torch.cuda.synchronize() +# measure +t0 = time.time() +for _ in range(1000): z = x @ y +torch.cuda.synchronize() +t1 = time.time() +# 计算带宽 +" +``` + +**方法2: 用现有 benchmark 工具** +- 检查 `/root/tools/` 下是否有带宽测试工具 +- 检查容器内是否有 `stream` 或 `bandwidthTest` 类似工具 + +**关键结论**: +- 实测带宽 vs H20 4.0 TB/s +- 如果 P800 带宽只有 ~1.3 TB/s,说明 3x 差距基本来自内存带宽 +- 如果带宽差距 < 2x,说明还有别的因素 + +### 实验 1: All-reduce 通信带宽基准 + +TP=8 要求每步做 All-reduce,通信开销占比很大。需要实测 XPULINK 带宽。 + +**方法1: BKCL bandwidth test** +```bash +# 在 8 卡容器内 +# 检查 /root/miniconda/envs/python310_torch25_cuda/bin 下是否有 bkcl 相关工具 +``` + +**方法2: 通过 TP 扩展性反推** +```bash +# 分别跑 TP=1, TP=2, TP=4, TP=8 +# 如果 TP=1→TP=2 的加速比接近 2x → 通信开销很小 +# 如果加速比远低于线性 → 通信是瓶颈 +``` + +**关键结论**: +- XPULINK 实测带宽 vs NVLink 900GB/s +- 如果 P800 卡间带宽远低于 NVLink,TP=8 的通信开销会占很大比例 + +### 实验 2: TP 扩展性曲线(直接在 P800 上测) + +在 P800 上跑 **TP=1, TP=2, TP=4, TP=8** 的 benchmark,固定总 batch size。 + +**为什么重要**: +- 如果 TP=1→TP=2 加速比接近 2x:通信效率高,差距在单卡能力 +- 如果 TP=1→TP=2 加速比仅 1.2x:通信/同步开销极大 +- C=1 时 TP=1 的 TPOT 是纯单卡 decode 延迟(无通信干扰) + +**执行方式**: +- 使用已有的 `dsv4_p800_sglang_tp_dp_matrix` 实验框架 +- 修改 `start_sglang_docker.sh` 的 TP 参数 +- C=1, ISL=4k, OSL=1k + +### 实验 3: Decode 耗时分解(手动分段计时) + +绕过 PyTorch Profiler 的崩溃问题,用**手动插桩计时**替代。 + +**在 SGLang 源码中的关键埋点**(通过 patch 文件实现,而非直接改容器内代码): + +``` +schedule(): + ├── wait_for_available_slots [调度等待] + ├── prepare_inputs [输入准备] + ├── forward(): + │ ├── attention (NSA) [注意力计算] + │ ├── MoE gating [路由] + │ ├── MoE expert forward [专家计算 + all-reduce] + │ ├── attention (decode) [解码注意力] + │ └── lm_head [输出头] + ├── all-reduce sync [同步] + └── stream output [输出] +``` + +**方法**: +- 在 `/data1/yy/sskj/platforms/patches/kunlun_p800/` 中创建 `manual_timing_patch.py` +- 在每个阶段前后用 `torch.cuda.Event` 记录时间 +- 输出每个阶段的时间占比 + +**参考理论预期**: +- 理想 Decode 中约 70-80% 时间在矩阵乘法(memory-bound) +- 约 10-20% 时间在通信(all-reduce) +- 约 5-10% 在其余操作 + +### 实验 4: Isolate 通信开销 - C=1 vs C=16 下不同 TP/DP 组合 + +| TP | DP | 每卡负载 | 通信量 | 预期效果 | +|:--:|:--:|:--------|:-------|:--------| +| 8 | 1 | 1/8 参数 | all-reduce 8卡 | 当前配置 | +| 4 | 2 | 1/4 参数 | all-reduce 4卡 | 通信减半 | +| 2 | 4 | 1/2 参数 | all-reduce 2卡 | 通信最少 | +| 1 | 8 | 全部参数 | 无 all-reduce | 无通信开销 | + +**分析**: +- 如果 TP=4/DP=2 比 TP=8/DP=1 快 → 通信开销过大 +- 如果 TP=1/DP=8 的 TPS > TP=8/DP=1 × 8 → 说明通信是主要瓶颈 +- 如果 TP 扩展性接近线性 → 通信不是瓶颈 + +### 实验 5: 对比分析 P800 vs H20 的算子实现差异 + +P800 使用 NSA attention + XPU 定制 kernel,H20 使用同一个 SGLang 框架的 CUDA 版本。 + +**需要检查的算子**: + +| 算子 | P800 实现 | H20 实现 | 可能的差距 | +|:----|:---------|:---------|:----------| +| NSA Attention (klxdsa) | XPU custom kernel | CUDA custom kernel | 优化程度差异 | +| MoE (experts) | XPU GEMM | CUDA GEMM (cuBLAS) | cuBLAS vs XPU BLAS | +| FP8/INT8 Quant | XPU custom | CUDA (CUTLASS) | 量化kernel效率 | +| All-reduce | BKCL (XPU) | NCCL (NVIDIA) | 通信库效率 | + +**方法**: +- 在容器内找到 `sglang/srt/layers/` 下的自定义算子实现 +- 对比 XPU 和 CUDA 的算子实现差异 +- 关注是否有明显未优化的 kernel(如用纯 PyTorch 而非融合 kernel) + +### 实验 6: 算子级微基准测试 + +编写独立的 micro-benchmark,测试关键操作的单次耗时: + +```python +for op in ['matmul', 'attention', 'rms_norm', 'all_reduce', 'silu_mul']: + for size in [1024, 2048, 4096, 8192]: + time_op(op, size, dtype=torch.float16) +``` + +**核心关注**: +- `matmul(M=1, N=4096, K=4096)` — Decode 的典型矩阵形状(batch=1) +- `all_reduce(8*4096)` — 单层通信量 +- `attention(Q=1, KV=model_dim)` — decode attention + +### 实验 7: SGLang 框架开销纯测 + +启动一个小模型(如 Llama-1B),在同样 TP 配置下跑同样的 benchmark。 + +**方法**: +- 在容器内用一个小模型跑同样的 bench_serving +- 对比 P800 和 H20 的框架调度延迟(如果能拿到 H20 数据) +- 如果小模型差距也 ~3x → 框架差异 +- 如果小模型差距 < 1.5x → 差距在算子/带宽 + +--- + +## 实验优先级和执行顺序 + +| 优先级 | 实验 | 耗时 | 最大信息价值 | +|:-----|:----|:----|:-----------| +| **P0** | 实验 0: 内存带宽实测 | 30min | 验证是否 3x 差距全来自 HBM 带宽 | +| **P0** | 实验 2: TP 扩展性 (TP=1,2,4,8) | 2h | 判断通信 vs 计算瓶颈 | +| **P1** | 实验 3: 手动分段计时 | 4h | 定位哪段算子最慢 | +| **P1** | 实验 4: 不同 TP/DP 组合对比 | 4h | 找到最优配置 | +| **P2** | 实验 5: 算子实现对比 | 2h | 确认是否有低效 kernel | +| **P2** | 实验 6: 算子微基准 | 3h | 精确量化每个算子的差距 | +| **P3** | 实验 7: 框架开销纯测 | 1h | 排除/确认框架影响 | + +--- + +## 数据归因分析 + +当所有实验完成后,3x 总差距可以归因到: + +``` +3.0x = A × B × C × D × E + +A = 内存带宽差距(HBM BW_P800 vs BW_H20) +B = 通信开销差距(XPULINK vs NVLink + BKCL vs NCCL) +C = 算子效率差距(XPU kernel vs CUDA kernel) +D = 量化效率差距(INT8 实现差异) +E = 框架调度差距(SGLang XPU vs CUDA 版本) +``` + +**预期判断**: +- 如果 A > 2.5x:根因是 P800 HBM 带宽不足(硬件天花板),优化空间有限 +- 如果 A < 1.5x 但 B + C > 2x:根因在通信/算子,有机会通过优化 kernel 缩小差距 +- 如果 C=1 和 C=16 差距一致:引擎调度不是问题 + +--- + +## 现有脚本复用 + +- **容器启动**: 复用 `dsv4_p800_sglang_tp_dp_matrix/start_sglang_docker.sh`(支持 TP/DP 参数) +- **Benchmark**: 复用 `run_bench.sh` 和已 patched 的 `bench_serving.py` +- **Patch 机制**: 通过 `/data1/yy/sskj/platforms/patches/kunlun_p800/` 挂载 +- **结果解析**: 复用 `scripts/common/compare.py` 和 `parse_backend.py` + +--- + +## 验证方式 + +每个实验的输出是**数值数据**(带宽 GB/s、延迟 ms、加速比): +1. 带宽测试跑 3 次取平均值,确认波动 < 5% +2. TP 扩展性实验检查 log 确认无异常(OOM、超时) +3. 手动分段计时的各阶段时间占比合理(总和 ≈ TPOT) +4. 与已知硬件理论值对比(H20 4.0 TB/s 作为标杆)