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