sskj/experiments/p800/P800_vs_H20_DIAGNOSIS_PLAN.md

8.5 KiB
Raw Blame History

P800 vs H20 性能差距根因诊断方案

Context

已知差距: DeepSeek-V4-Flash-INT8 模型在 TP8/DP1, ISL=4k, OSL=1k, C=16 下P800 比 H20 慢 ~3xTPS: 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 测试

# 在 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/ 下是否有带宽测试工具
  • 检查容器内是否有 streambandwidthTest 类似工具

关键结论:

  • 实测带宽 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

# 在 8 卡容器内
# 检查 /root/miniconda/envs/python310_torch25_cuda/bin 下是否有 bkcl 相关工具

方法2: 通过 TP 扩展性反推

# 分别跑 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测试关键操作的单次耗时

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.pyparse_backend.py

验证方式

每个实验的输出是数值数据(带宽 GB/s、延迟 ms、加速比

  1. 带宽测试跑 3 次取平均值,确认波动 < 5%
  2. TP 扩展性实验检查 log 确认无异常OOM、超时
  3. 手动分段计时的各阶段时间占比合理(总和 ≈ TPOT
  4. 与已知硬件理论值对比H20 4.0 TB/s 作为标杆)