Prhub

#6405 [fsdp] fix: sort buffers in fsdp2_load_full_state_dict to prevent NCCL deadlock with heterogeneous buffers

原始 PR 作者 nanastassacos 合并时间 2026-05-21 10:04 文件变更 1 提交数 3 评论 5 代码增减 +6 / -1

执行摘要

修复 FSDP2 异构缓冲区 NCCL 死锁

Gemma4 等模型具有异构缓冲区大小(如全注意力 RoPE 256 元素、滑动注意力 RoPE 128 元素),FSDP2 包装后 model.named_buffers() 在多个 rank 上的返回顺序不确定,导致不同 rank 在同一通信集体中广播大小不匹配的张量,引发 NCCL 死锁。关联 Issue #6365 详细描述了该问题,PR body 也明确说明了 root cause。

值得精读:该 PR 揭示了 FSDP2 包装后 named_buffers() 顺序不确定这一容易被忽视的坑,并提供了一种简单有效的修复模式。对于计划支持更多异构模型架构的团队尤其有参考价值。

讨论亮点

review 中 gemini-code-assist[bot] 提出将 import warnings 移到文件顶部以符合 PEP 8 规范;但后续提交表明该建议没有被采纳(可能是因为改动已合并或决定保持现状)。此外,wuxibin89 要求格式化代码和解决冲突,作者均已处理。

实现拆解

  1. 排序缓冲区广播顺序:在 verl/utils/fsdp_utils.pyfsdp2_load_full_state_dict 函数中,将原先直接遍历 model.named_buffers() 改为先通过 sorted() 按名称排序,再循环广播。这一改动确保所有 rank 按相同顺序处理缓冲区,彻底避免因异构缓冲区大小不匹配导致的 NCCL 死锁。
  2. 放宽缺失层类处理:在 _select_fsdp2_wrap_targets 函数中,将找不到配置的 transformer layer class 时的行为从 raise Exception 改为 warnings.warn 并回退到基于大小的自动包装策略。这使得多模态模型在纯文本模式下不再因缺失视觉/音频层而崩溃。
文件 模块 状态 重要度
verl/utils/fsdp_utils.py 工具函数 modified 6.1

关键符号

fsdp2_load_full_state_dict _select_fsdp2_wrap_targets

关键源码片段

verl/utils/fsdp_utils.py core-logic

核心变更文件。包含两个关键改动:排序缓冲区广播顺序修复 NCCL 死锁;放宽缺失层类处理以支持多模态模型。

# verlag/utils/fsdp_utils.pydef fsdp2_load_full_state_dict(model: torch.nn.Module, full_state: dict, device_mesh=None, cpu_offload=None):
    # ... ( 前面的版本检查和 set_model_state_dict 调用保持不变 ) ...
​
    # rotary_emb 不在 state_dict 中,需要手动广播
    # 按名称排序以确保跨 rank 顺序确定。FSDP2 可能导致 named_buffers()
    # 在不同 rank 上返回不同顺序。Gemma4 具有异构 RoPE 缓冲区(256 vs 128 元素),
    # 因此顺序不匹配会导致同一广播集合上大小不匹配,进而引发 NCCL 死锁。
    bufs = sorted(model.named_buffers(), key=lambda x: x[0])
    for name, buf in bufs:
        dist.broadcast(buf, src=0)
​
    if cpu_offload:
        model.to("cpu", non_blocking=True)
        for buf in model.buffers():
            buf.data = buf.data.to(get_device_id())

评论区精华

import warnings 位置 style

gemini-code-assist[bot] 建议将 import warnings 移到文件顶部以符合 PEP 8。

结论:建议未被采纳,当前实现保持 import 在函数内。 · 没有解决

风险与影响

改动仅涉及两个函数,逻辑清晰且影响范围小。排序操作会增加 O(n log n) 开销,但 named_buffers() 返回的缓冲区数量通常较少(Gemma4 模型约为 35 个),性能影响可忽略。放宽缺失层类处理可能会掩盖某些配置错误(如拼写错误的类名),但通过 warning 日志仍可追踪。

直接影响:修复了 Gemma4 等异构注意力模型在 FSDP2 下初始化时卡死的问题,使这些模型能够正常启动训练。间接影响:所有使用 fsdp2_load_full_state_dict 的模型(Qwen, Llama 等现有模型)不受影响,因为排序对于均匀大小的缓冲区不会产生行为差异。放宽缺失层类处理有助于多模态模型在纯文本模式下运行,但暂不影响现有模型。

多 rank 环境不易测试

关联 Issue

#6365 [bug][fsdp2] NCCL deadlock in fsdp2_load_full_state_dict with heterogeneous buffer sizes (Gemma4)

完整报告

参与讨论