已开启
[Benchmark] Qwen3-8B-lite fully_shard 性能对比(torch_npu 原生 vs hyper torch/ms) #192
changzherui创建于  6月8日
changzherui
changzherui成员
6月8日 创建

Qwen3-8B-lite fully_shard 性能优化实验全记录

Issue 类型:Benchmark + Profiler 瓶颈分析 + 源码优化验证
测试目录:dev/test/qwen3_8b_hsdp2/
环境:Ascend NPU 8 卡,CANN 9.0,seq_len=4096,batch=1,steps=20/warmup=2
最后更新:2026-06-08(含 P12 norm/lm_head 拆 wrap 负向验证)


零、执行摘要

维度 结论
最优配置 hyper torch + prefetch(1/1) + PR707 → 542 ms/step(FSDP8)
vs 原生 快 3.7%(原生 563 ms);HSDP24 prefetch 后 持平(790 vs 791 ms)
根因 hyper 默认慢 不在计算(Computing 反而快 47 ms),在 通信重叠率低(4.6% vs 82.6%)+ per-param collective 碎片化(360 AG vs 38)
关键开关 prefetch 是质变(-17%);comm_fusion 单独开更慢;P0/P1 让 cf+prefetch 追平原生
残余热点 post_backward:norm(~190 ms,不宜拆 wrap)、pre_backward prefetch:layers.7(~107 ms)

性能演进(FSDP8 hyper torch,ms/step):

默认 658 ──prefetch──► 544 ──PR707──► 542  ⭐ 当前最优
         │
         └──comm_fusion 单独──► 701(更慢)
         └──cf+prefetch──► 581 ──P0──► 568 ──P1──► 565 ≈ 原生 563

一、测试方法与配置

1.1 对比路径

标识 后端 说明
原生 torch_npu.distributed.fsdp.fully_shard NPU 原生 FSDP2
hyper torch hyper_parallel + HYPER_PARALLEL_PLATFORM=torch 本 Issue 主要优化对象
hyper MS hyper_parallel + MindSpore 后端 参考对比

1.2 统一参数

项 值
模型 Qwen3-8B-lite:dim=4096,layers=8,ffn=12288,32 heads / 8 kv
Mesh FSDP8 (8,) / HSDP24 (2,4)
精度 bf16 param + fp32 reduce
优化器 AdamW(lr=1e-4),weight_decay=0
对齐 align_data_8b/:init_weights.pt + preset_batches.pt,seed=42
计时 warmup 后整步 torch.npu.synchronize() 平均

1.3 Profiler 采集

  • torch_npu.profiler,Level0,with_stack=False
  • schedule(wait=2, warmup=1, active=2, repeat=1) → 分析 rank0 step 3–4
  • 数据源:ASCEND_PROFILER_OUTPUT/step_trace_time.csv + operator_details.csv

1.4 开关说明

开关 含义
prefetch 正反向各预取 1 层(set_modules_to_forward/backward_prefetch)
comm_fusion 模块级 fused AG/RS(collective 次数 360→38)
P0/P1 本地 hyper_parallel/platform/torch/fully_shard/ 源码优化
PR707 hsdp_scheduler.py:hook 顺序 unshard → prefetch(PR #707)

二、实验全景(11 项 e2e + 11 项 profiler)

2.1 端到端实验(无 Profiler,steps=20)

ID 实验 Mesh ms/step vs 原生 结果文件
T1 原生 默认 FSDP8 563.08 — baseline_8card/torch_native_8b_8card_fsdp_s4096.json
T2 hyper 默认 FSDP8 657.88 +17% pure_fsdp2_8card/hyper_torch_8b_8card_fsdp_s4096.json
T3 hyper MS 默认 FSDP8 698.16 +24% pure_fsdp2_8card/mindspore_8b_8card_fsdp_s4096.json
T4 hyper +prefetch FSDP8 543.59 -3.5% hyper_torch_prefetch/..._prefetch.json
T5 hyper +prefetch PR707 FSDP8 542.23 -3.7% ⭐ pr707_hyper_torch_prefetch/..._pr707.json
T6 hyper +comm_fusion FSDP8 700.80 +24% hyper_torch_comm_fusion/..._comm_fusion.json
T7 原生 默认 HSDP24 791.28 — torch_native_hsdp24/..._foreach_false.json
T8 hyper 默认 HSDP24 1028.15 +30% baseline_8card/torch_align_..._hsdp24.json
T9 hyper MS 默认 HSDP24 1051.14 +33% baseline_8card/mindspore_align_..._hsdp24.json
T10 hyper +prefetch HSDP24 789.67 -0.2% hyper_torch_prefetch/..._hsdp24_prefetch.json
T11 hyper +comm_fusion HSDP24 1100.49 +39% hyper_torch_comm_fusion/..._hsdp24_comm_fusion.json

2.2 Profiler 实验(steps=8,含 profiler 开销)

ID 实验 prof e2e Stage Compute Comm未重叠 重叠率 AG/RS 目录
P1 原生 FSDP8 566.50 566.8 521.8 40.7 82.6% 38/40 profiler/torch_native_fsdp8/
P2 hyper 无 prefetch 660.46 662.9 474.7 181.5 4.6% 360/364 profiler/hyper_torch_fsdp8/
P3 hyper +prefetch 546.03 549.5 492.4 50.2 80.1% 360/364 profiler/hyper_torch_fsdp_prefetch/
P4 hyper +prefetch PR707 544.70 551.5 492.9 51.7 79.8% 360/364 profiler/hyper_torch_fsdp_prefetch_pr707/
P5 hyper +prefetch fwd1/bwd2 544.56 551.0 491.7 52.0 80.0% 360/364 profiler/hyper_torch_fsdp_prefetch_fwd1_bwd2/
P12 split norm/lm_head ❌ 570.71 574.7 486.6 82.7 66.0% 360/364 profiler/hyper_torch_fsdp_prefetch_split_norm_lm_head/
P6 cf+prefetch 优化前 581.05 584.5 532.0 46.7 81.2% 38/40 profiler/..._comm_fusion_prefetch/
P7 cf+prefetch P0 568.45 570.6 519.3 44.3 81.5% 38/40 profiler/..._prefetch_p0/
P8 cf+prefetch P1 565.31 567.5 515.1 46.8 80.5% 38/40 profiler/..._prefetch_p1/
P9 原生 HSDP24 792.41 798.6 626.1 165.6 71.0% 38/40 profiler/torch_native_hsdp24/
P10 hyper 无 prefetch 1025.78 1028.9 515.6 505.4 32.3% 360/364 profiler/hyper_torch_hsdp24/
P11 hyper +prefetch 793.15 801.0 570.3 224.2 62.9% 360/364 profiler/hyper_torch_hsdp24_prefetch/

三、优化过程(按阶段)

阶段 0:基线对比(T1/T2/T7/T8)

发现:hyper 默认比原生慢 17%(FSDP8)/ 30%(HSDP24),但 loss 完全对齐。

Mesh 原生 hyper 默认 差距
FSDP8 563 ms 658 ms +95 ms
HSDP24 791 ms 1028 ms +237 ms

阶段 1:Profiler 根因定位(P1/P2/P9/P10)

假设:hyper 计算慢?→ 否定。

FSDP8 rank0 分解(step 3–4):

指标 原生 P1 hyper 无 prefetch P2 Δ
Stage 566.8 ms 662.9 ms +96 ms
Computing 521.8 ms 474.7 ms -47 ms
Comm(未重叠) 40.7 ms 181.5 ms +141 ms
Comm(已重叠) 193.6 ms 8.7 ms -185 ms
重叠率 82.6% 4.6% —
collective 38 AG / 40 RS 360 AG / 364 RS 9× 碎片化
Stage 差距 +96 ms ≈ Computing -47 ms + Comm(未重叠) +141 ms

HSDP24 同理:hyper Computing 515.6 ms < 原生 626.1 ms,但 Comm(未重叠) 505.4 ms >> 原生 165.6 ms。

根因闭环:逐参数 collective + 无 prefetch → 通信几乎不与计算重叠。

阶段 2:开启 prefetch(T4/T10,P3/P11)

措施:train_hyper_torch.py 增加 --prefetch,正反向各 1 层。

Mesh 无 prefetch +prefetch 变化 重叠率
FSDP8 e2e 658 ms 544 ms -17% 4.6%→80.1%
HSDP24 e2e 1028 ms 790 ms -23% 32.3%→62.9%

prefetch 是质变级优化:FSDP8 超越原生,HSDP24 追平。

阶段 3:comm_fusion 路径优化(P6→P7→P8,P0/P1 源码)

背景:comm_fusion 单独开启更慢(T6: 701 ms);但 collective 次数与原生对齐(38/40),适合大模型。

P0 措施(state.py, param_group.py):

  • post_backward_for_comm_fusion() 流水线重排:先 issue 当前层 RS,再 drain 上一层
  • 每层 _reduce_scatter_handle 独立存储
  • reduce_scatter_copy_in():torch.cat + 单次 copy
  • ensure_flat_param_buffer() 延迟初始化

P1 措施(param_group.py, param.py, state.py):

  • foreach_all_gather_copy_out() AG view 零拷贝
  • release_unshard_buffers() 在 shard() 时释放
  • _reduce_scatter_input 跨 step 复用
阶段 prof e2e vs 优化前 关键算子变化
cf+prefetch 优化前 P6 581.05 ms — split_with_sizes_copy 45.6 ms
P0 P7 568.45 ms -12.6 ms aten::copy_ 153→115 ms
P1 P8 565.31 ms -3.1 ms split_with_sizes_copy →0 ms
原生 P1 566.50 ms 参考 —
prefetch only P3 546.03 ms 仍最快 —

阶段 4:调度微调(P4/P5,PR707 + prefetch 深度)

PR #707:hsdp_scheduler.py forward/backward pre-hook 从 prefetch→unshard 改为 unshard→prefetch。

指标 P3 旧顺序 P4 PR707 Δ
e2e(无 profiler) 543.59 ms 542.23 ms -1.36 ms
prof e2e 546.03 ms 544.70 ms -1.33 ms
unshard:tok_embeddings device 38.7 ms 35.8 ms -2.9 ms
prefetch:layers.7 device 107.2 ms 107.5 ms 不变

prefetch 深度扫描 P5(fwd=1, bwd=2):

配置 prof e2e 重叠率
fwd1/bwd1 P3 546.03 ms 80.1%
fwd1/bwd2 P5 544.56 ms 80.0%

加深 prefetch 无收益(差 0.3%,噪声内)。

阶段 5:norm/lm_head 拆 wrap 验证(P12)❌ 负向结果

假设:post_backward:norm(~186 ms)因 norm 与 lm_head 绑在同一 FSDP 单元,RS 粒度过粗;拆分后可降低尾部 RS 耗时。

措施:train_hyper_torch.py 新增 --split-norm-lm-head,将

fully_shard([norm, lm_head])  # 默认

改为

fully_shard(norm); fully_shard(lm_head)  # 各自独立 wrap

配置:FSDP8 + prefetch + PR707,其余与 P4 相同。

指标 P4 绑定 norm+lm_head P12 拆分 Δ
prof e2e 544.70 ms 570.71 ms +26.0 ms(慢 4.8%)
Stage 551.5 574.7 +23.2
Comm(未重叠) 51.7 82.7 +31.0
重叠率 79.8% 66.0% -13.8 pp
AG/RS 次数 360/364 360/364 不变

Hook 变化(device ms,rank0,2 step):

Hook P4 绑定 P12 拆分
post_backward:norm 185.6 47.6
post_backward:lm_head (含在 norm 内) 278.5
norm + lm_head 合计 ~186 ~326
post_backward:layers.7 19.8 75.8

根因:

  1. 绑定时 norm+lm_head 一次 RS(~186 ms),可与计算较好重叠
  2. 拆分后变成 两次串行 RS(48 + 279 ms),合计更高且更难重叠
  3. wrap 单元 11→12,prefetch 链被打断,layers.7 post_backward 从 20→76 ms
  4. lm_head 参数量远大于 norm,不应单独拆出,绑定反而更优

结论:❌ 保持默认 fully_shard([norm, lm_head]),不要拆分。


四、Profiler 深度分析

4.1 FSDP8 配置横向对比(rank0)

配置 Stage Compute Comm未重叠 Comm未重叠占Stage 重叠率
原生 566.8 521.8 40.7 7.2% 82.6%
hyper 无 prefetch 662.9 474.7 181.5 27.4% 4.6%
+prefetch 549.5 492.4 50.2 9.1% 80.1%
+prefetch PR707 551.5 492.9 51.7 9.4% 79.8%
cf+prefetch P1 567.5 515.1 46.8 8.2% 80.5%

4.2 HSDP24 配置横向对比(rank0)

配置 Stage Compute Comm未重叠 重叠率
原生 798.6 626.1 165.6 71.0%
hyper 无 prefetch 1028.9 515.6 505.4 32.3%
+prefetch 801.0 570.3 224.2 62.9%

HSDP prefetch 后 Comm(未重叠) 仍高于原生(224 vs 166 ms),是 HSDP 残余差距来源。

4.3 Hook 热点(device 累计 ms,2 step,rank0)

Hook 无 prefetch P2 +prefetch P3 PR707 P4 split P12 cf+P1 P8
post_backward:norm 280 185 186 48 190
post_backward:lm_head — — — 279 —
pre_backward prefetch:layers.7 — 107 108 108 114
post_backward:layers.7 ~80 ~20 20 76 —
post_backward:layers.*(其他层) ~80 ~60 ~61 ~60 ~65
HSDP post_backward:layers.7 707 521 — — —

解读:

  • prefetch 将 norm RS 从 280→185 ms;profiler 记为 post_backward:norm 实为 norm+lm_head 绑定单元的一次 RS
  • 拆分 norm/lm_head(P12)反而更慢:两次 RS 合计 ~326 ms,重叠率降至 66%
  • layers.7 backward prefetch AG 是固定尾部开销(~107 ms)
  • HSDP 无 prefetch 时 layers.7 post_backward 极高(707 ms),prefetch 后大幅缓解

五、结论与配置推荐

5.1 核心结论

  1. mesh:纯 FSDP8 比 HSDP24 快 ~1.4×,公平对比优先 FSDP8。
  2. prefetch 必开:默认配置不可用(慢 17–30%);开启后 FSDP8 超原生。
  3. comm_fusion 单独开有害;配合 P0/P1 + prefetch 可与原生持平,但不如 prefetch-only 快。
  4. PR707 建议合入:小幅收益(~1.3 ms),首层 unshard 阻塞减少。
  5. prefetch 深度 1/1 足够;norm/lm_head 保持绑定 wrap(拆分实测慢 4.8%);HSDP replicate AR 待优化。

5.2 配置推荐

场景 推荐 预期 ms/step
FSDP8 极致性能 prefetch=True, comm_fusion=False(+PR707) ~542
对齐原生 collective 次数 / 大模型 prefetch=True, comm_fusion=True(P0/P1) ~565
HSDP24 追平原生 prefetch=True ~790
❌ 不推荐 默认无 prefetch / 单独 comm_fusion / split norm+lm_head 658–1100 / 571

六、复现命令

source ~/env.sh
cd dev/test/qwen3_8b_hsdp2

# ── 端到端 ──
./run_baseline_8card.sh          # T1/T8 等基线
./run_pure_fsdp2_8card.sh        # T2/T3 hyper 默认
./run_hyper_torch_prefetch.sh    # T4/T10 +prefetch
./run_hyper_torch_comm_fusion.sh # T6/T11 comm_fusion

# ── PR707 验证(本地 hyper-parallel 已 apply patch)──
python train_hyper_torch.py --nproc 8 --mesh fsdp --prefetch --prefetch-layers 1 \
  --align-dir align_data_8b --steps 20 \
  --output-json results/pr707_hyper_torch_prefetch/out.json

# ── Profiler ──
./run_torch_native_fsdp8_profile.sh       # P1
./run_hyper_torch_fsdp8_profile.sh        # P2(若脚本存在)
./run_hyper_torch_prefetch_profile.sh fsdp  # P3
./run_hyper_torch_comm_fusion_prefetch_profile.sh fsdp  # P6/P7/P8
./run_torch_native_hsdp24_profile.sh    # P9
./run_hyper_torch_hsdp24_profile.sh     # P10/P11

# ── prefetch 深度扫描 ──
python train_hyper_torch.py --nproc 8 --mesh fsdp --prefetch \
  --prefetch-forward-layers 1 --prefetch-backward-layers 2 \
  --npu-profiler --profiler-dir results/profiler/hyper_torch_fsdp_prefetch_fwd1_bwd2 ...

七、结果文件索引

results/
├── baseline_8card/              # T1 原生 FSDP8, T8 hyper HSDP24
├── pure_fsdp2_8card/            # T2/T3 hyper 默认 FSDP8
├── hyper_torch_prefetch/        # T4/T10 +prefetch e2e
├── pr707_hyper_torch_prefetch/  # T5 PR707 e2e
├── hyper_torch_comm_fusion/     # T6/T11 comm_fusion e2e
├── torch_native_hsdp24/         # T7 原生 HSDP24
└── profiler/
    ├── torch_native_fsdp8/              # P1
    ├── hyper_torch_fsdp8/               # P2
    ├── hyper_torch_fsdp_prefetch/        # P3
    ├── hyper_torch_fsdp_prefetch_pr707/  # P4
    ├── hyper_torch_fsdp_prefetch_fwd1_bwd2/  # P5
    ├── hyper_torch_fsdp_prefetch_split_norm_lm_head/  # P12 ❌
    ├── hyper_torch_fsdp_comm_fusion_prefetch/     # P6
    ├── hyper_torch_fsdp_comm_fusion_prefetch_p0/  # P7
    ├── hyper_torch_fsdp_comm_fusion_prefetch_p1/  # P8
    ├── torch_native_hsdp24/              # P9
    ├── hyper_torch_hsdp24/               # P10
    └── hyper_torch_hsdp24_prefetch/      # P11

八、后续 TODO


本文档由 dev/test/qwen3_8b_hsdp2/ 全部实验数据自动汇总生成。

likedislike
changzheruichangzherui成员
6月8日 修改标题为 “[Benchmark] Qwen3-8B-lite fully_shard 性能对比(torch_npu 原生 vs hyper torch/ms)”,原标题为“[Benchmark] Qwen3-8B-lite fully_shard:torch_npu 原生 vs hyper (torch/ms) 性能对比”
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 将 changzherui1 设为负责人
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述
changzheruichangzherui成员
6月8日 修改了issue 的描述