分布式 group 初始化与通信边界#

这章解决什么问题#

运行时架构前面已经讲了 rank 计算、TpModelWorkerDataParallelControllerServerArgs 的人格编译,但还缺一层非常重要的“把这些并行意图真的落成通信组”的路径:distributed group 到底在哪里初始化?TPPP、world group 这些对象什么时候真的变成 PyTorch 通信边界?

这一章就是把这条分布式初始化链讲清楚。

为什么这层值得单独成章#

因为很多多机/多并行问题如果只看 rank 数学还是不够。你还需要知道:

  • group 什么时候初始化
  • 哪些组件会直接拿 get_tp_group() / get_pp_group() / get_world_group()
  • graph capture、通信融合、worker 初始化都怎样依赖这些 group

也就是说,这一层是“并行身份”进一步变成“通信现实”的桥。

一张图:从 rank 身份到通信边界,还要再过一层 distributed init#

这张图解决的理解障碍是:很多读者会把 rank 计算完就当作一切都到位,而实际上还要真的初始化 group。

flowchart LR
    Rank["tp/pp/dp/ep identity"] --> Dist["init_distributed_environment()"]
    Dist --> MP["initialize_model_parallel()"]
    MP --> Groups["world / tp / pp groups"]
    Groups --> Worker["scheduler / tp_worker / runner"]

图比纯文字多解释的一点是:rank 身份只是静态标签,group 初始化才是把它变成可通信对象的那一步。

parallel_state.py 为什么是这条路径的主锚点#

这里最值得记住的几个入口是:

  • init_distributed_environment(...)
  • initialize_model_parallel(...)
  • get_world_group()
  • get_tp_group()
  • get_pp_group()

这说明整套分布式初始化并不是分散在各个 worker 里偷偷进行,而是已经被集中收口到 parallel state 层。

为什么 TpModelWorker 很适合当这条路径的第二入口#

因为它会在初始化之后显式拿:

  • self.pp_group = get_pp_group()
  • self.world_group = get_world_group()

这说明 worker 并不自己发明通信边界,而是消费已经初始化好的 group 对象。也就是说,parallel state 层负责生成通信边界,worker 层负责使用它们。

Scheduler 这一侧又怎样消费这些 group#

scheduler.py 里同样会拿:

  • self.tp_group = get_tp_group()
  • self.pp_group = get_pp_group()
  • self.world_group = get_world_group()

这说明 scheduler 对这些 group 的依赖不仅是“为了 forward”,还影响:

  • result broadcast
  • overlap / pp 路径
  • various control-message handling

也就是说,group 边界并不是执行层专属,调度层本身也在消费它们。

为什么这层会和 graph capture / communication op 形成回扣#

一旦 get_tp_group() / get_pp_group() 变成稳定对象,后面很多能力都会建立在其上:

  • communication_op.py 里的 all-reduce / all-gather
  • graph capture 里的 group-aware context
  • worker 内部一些 fused communication path

这说明 distributed init 不是启动时的一次性动作,而是后续很多执行优化能否成立的前提。

这一层最容易出现的误判#

1. 以为 rank 算出来就等于 group 已经存在#

实际还要经过 distributed init 和 model parallel 初始化。

2. 以为 group 只服务 forward#

scheduler 和 worker 的很多控制路径也依赖它们。

3. 以为 distributed init 只是启动时一次性噪声#

它其实会影响后面一整条通信边界。

如果你怀疑问题和 group 边界有关,先怎么查#

建议按这个顺序:

  1. 先确认 rank 身份本身是否算对。
  2. 再确认 distributed environment 与 model parallel 是否真的初始化成功。
  3. 再看 scheduler / worker / execution 代码在哪些地方消费这些 group。
  4. 最后才深入某个具体通信算子或 graph capture 细节。

小结#

这一章真正想补齐的,是运行时架构里经常被跳过的一层“通信现实”:

  • rank 身份只是标签
  • distributed group 初始化才把这些标签变成真正可用的通信边界
  • 只有把这层桥讲清,多并行运行时在源码里才不会显得像一团魔法

到这里,运行时架构对并行系统的解释也开始从“谁是谁”推进到“它们怎样真的连起来”。