feat: add P800 vs H20 diagnosis plan for performance gap analysis

This commit is contained in:
yy-fighting 2026-07-20 05:23:49 +00:00
parent a7e2037471
commit 0afd552ad3

View File

@ -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.9x26.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 卡间带宽远低于 NVLinkTP=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 定制 kernelH20 使用同一个 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 作为标杆)