sskj/experiments/p800/P800_vs_H20_DIAGNOSIS_PLAN.md

237 lines
8.5 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.

# 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 作为标杆