3.2 拓扑感知调度:NVLink、PCIe 与 RDMA 亲和


文档摘要

3.2 拓扑感知调度:NVLink、PCIe 与 RDMA 亲和 同样是 8 卡训练,把 8 张卡放在「全 NVLink 互联」的节点上,AllReduce 通信可能比放在「走 PCIe Switch」的节点上快 3-5 倍。调度器如果不懂拓扑,盲目把训练 Pod 撒到任意节点,等于让法拉利跑乡道。拓扑感知调度的任务,就是让分布式训练的通信走最快的路。 3.2.1 多 GPU 节点的拓扑真相 一个 8 卡 GPU 服务器内部,8 张卡并不是「两两等距」的。它们通过几种不同的互联通道连接,每种通道的带宽与延迟差异巨大: NVLink:NVIDIA 专属的高速 GPU 互联,单链路带宽可达 50-300 GB/s(H100 NVLink 4.

同样是 8 卡训练,把 8 张卡放在「全 NVLink 互联」的节点上,AllReduce 通信可能比放在「走 PCIe Switch」的节点上快 3-5 倍。调度器如果不懂拓扑,盲目把训练 Pod 撒到任意节点,等于让法拉利跑乡道。拓扑感知调度的任务,就是让分布式训练的通信走最快的路。

3.2.1 多 GPU 节点的拓扑真相

一个 8 卡 GPU 服务器内部,8 张卡并不是「两两等距」的。它们通过几种不同的互联通道连接,每种通道的带宽与延迟差异巨大:

  • NVLink:NVIDIA 专属的高速 GPU 互联,单链路带宽可达 50-300 GB/s(H100 NVLink 4.0 单向 450 GB/s),是 GPU 间通信的首选。
  • PCIe(Peripheral Component Interconnect Express):通用总线,PCIe 4.0 x16 双向约 64 GB/s,远低于 NVLink。
  • NUMA(Non-Uniform Memory Access):CPU 与内存的拓扑分组,跨 NUMA 节点访问内存慢于本 NUMA。
  • InfiniBand / RoCE:节点间的高速网络,用于跨节点分布式训练通信。

一个典型 8 卡节点的拓扑(示意):

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 380" font-family="sans-serif" font-size="12"> <text x="440" y="22" text-anchor="middle" font-size="15" font-weight="bold">8 卡 GPU 节点内部拓扑示意(NUMA + NVLink + IB)</text> <!-- NUMA 0 --> <rect x="20" y="50" width="410" height="200" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-dasharray="4,2"/> <text x="35" y="72" font-weight="bold" fill="#1e40af">NUMA 0(CPU 0 + 本地内存)</text> <g> <rect x="40" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#2563eb"/> <text x="80" y="115" text-anchor="middle">GPU 0</text> <rect x="135" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#2563eb"/> <text x="175" y="115" text-anchor="middle">GPU 1</text> <rect x="230" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#2563eb"/> <text x="270" y="115" text-anchor="middle">GPU 2</text> <rect x="325" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#2563eb"/> <text x="365" y="115" text-anchor="middle">GPU 3</text> <!-- NVLink 横向连接 --> <line x1="120" y1="115" x2="135" y2="115" stroke="#dc2626" stroke-width="2"/> <line x1="215" y1="115" x2="230" y2="115" stroke="#dc2626" stroke-width="2"/> <line x1="310" y1="115" x2="325" y2="115" stroke="#dc2626" stroke-width="2"/> <text x="80" y="170" text-anchor="middle" font-size="10" fill="#dc2626">━ NVLink(极快)</text> <text x="225" y="210" text-anchor="middle" font-size="10" fill="#475569">GPU 0-3 之间全互联 NVLink</text> </g> <!-- NUMA 1 --> <rect x="450" y="50" width="410" height="200" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-dasharray="4,2"/> <text x="465" y="72" font-weight="bold" fill="#15803d">NUMA 1(CPU 1 + 本地内存)</text> <g> <rect x="470" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#16a34a"/> <text x="510" y="115" text-anchor="middle">GPU 4</text> <rect x="565" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#16a34a"/> <text x="605" y="115" text-anchor="middle">GPU 5</text> <rect x="660" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#16a34a"/> <text x="700" y="115" text-anchor="middle">GPU 6</text> <rect x="755" y="90" width="80" height="50" rx="4" fill="#fff" stroke="#16a34a"/> <text x="795" y="115" text-anchor="middle">GPU 7</text> <line x1="550" y1="115" x2="565" y2="115" stroke="#dc2626" stroke-width="2"/> <line x1="645" y1="115" x2="660" y2="115" stroke="#dc2626" stroke-width="2"/> <line x1="740" y1="115" x2="755" y2="115" stroke="#dc2626" stroke-width="2"/> <text x="795" y="170" text-anchor="middle" font-size="10" fill="#dc2626">━ NVLink(极快)</text> </g> <!-- 跨 NUMA --> <line x1="430" y1="115" x2="450" y2="115" stroke="#ca8a04" stroke-width="3"/> <text x="440" y="100" text-anchor="middle" font-size="10" fill="#ca8a04">QPI</text> <text x="440" y="135" text-anchor="middle" font-size="10" fill="#ca8a04">慢</text> <!-- IB 网卡 --> <rect x="350" y="280" width="180" height="50" rx="6" fill="#fce7f3" stroke="#db2777"/> <text x="440" y="310" text-anchor="middle" font-weight="bold">InfiniBand HCA(跨节点)</text> <line x1="225" y1="250" x2="400" y2="280" stroke="#475569" stroke-dasharray="3,3"/> <line x1="650" y1="250" x2="480" y2="280" stroke="#475569" stroke-dasharray="3,3"/> <text x="20" y="355" font-size="11" fill="#475569">关键:GPU 0-3 在同一 NUMA 且 NVLink 全互联 → 通信最快;跨 NUMA 走 QPI 变慢;跨节点走 IB</text> </svg>

这张图揭示了一个关键事实:即使是同一节点内的 8 张卡,通信速度也千差万别。GPU 0 和 GPU 1 之间走 NVLink(极快),GPU 0 和 GPU 4 之间跨 NUMA 走 QPI(慢),GPU 0 和另一节点上的 GPU 走 IB(中等)。

3.2.2 拓扑对分布式训练的影响:以 AllReduce 为例

分布式训练最核心的通信原语是 AllReduce(详见第 5 章)——所有 worker 把各自的梯度聚合后广播给所有人。AllReduce 的通信量与拓扑带宽直接相关。

假设一个 8 卡训练,每张卡要把 1GB 梯度做 AllReduce。理论上:

  • 全 NVLink:带宽 300 GB/s,理论耗时约 3ms(1GB × 8 / 300 GB/s 数量级)。
  • 走 PCIe:带宽 64 GB/s,耗时约 5 倍。
  • 跨节点走 IB:200 Gbps(25 GB/s),更慢,还要加跨节点延迟。

💡 判读:拓扑差异在单次 AllReduce 上看是「快几倍」的差异,但在一个跑几万步、每步都要 AllReduce 的训练里,累积起来就是「训练一天 vs 训练一周」的差距。这就是为什么大模型训练对拓扑极其敏感。

更复杂的并行策略对拓扑的要求更苛刻:

并行策略 通信特征 拓扑要求
数据并行(DP) AllReduce 梯度 越快越好,但相对宽容
张量并行(TP) 每层多次 AllReduce 必须 NVLink,否则极慢
流水线并行(PP) 跨 stage 传激活 跨节点可接受,但带宽要够

第 5 章会深入讲这些并行策略。这里只需记住:张量并行几乎只能在同节点 NVLink 内做,跨节点张量并行会慢到无法接受。这就是为什么调度器必须懂拓扑。

3.2.3 拓扑感知调度的核心机制

拓扑感知调度(Topology-Aware Scheduling) 的目标是:在调度分布式训练的多个 Pod 时,优先把它们放到通信最快的拓扑位置。它的核心是三步:

第一步:拓扑发现

每个节点的拓扑信息可以通过几种方式获取:

  • nvidia-smi topology -m:给出 GPU 之间的互联方式(NVLink、PIX、PHB、SYS 等)。
  • NUMA 节点信息:从 /sys/bus/pci/devices/lscpu 读取。
  • 网络拓扑:节点的机架(rack)、可用区(zone)、IB 子网。

NVIDIA 的 GPU Operator 会把这些信息以节点标签或资源的形式上报,供调度器查询。典型的 GPU 互联距离编码(NVIDIA 的 topology -m 输出):

编码 含义 通信代价
NV 同 NVLink/NVSwitch 极低(最优)
PIX 同 PCIe Switch
PHB 同 PCIe Host Bridge 但不同 Switch
SYS 跨 NUMA 节点(QPI/UPI)
NODE 跨节点 最高(走 IB/以太网)

第二步:分值计算

调度器拿到拓扑信息后,要把它转成「打分」。一个简化的打分算法(实际调度器更复杂):

score(node, pod) = Σ w_i * affinity_i 其中 affinity_i 是 pod 对节点上已有/将有的 GPU 的拓扑亲和度: NV → 100 分 PIX → 80 分 PHB → 60 分 SYS → 20 分 NODE → 0 分

权重 w_i 可调,比如张量并行训练把 NV 权重提到极高,强制把 worker 调度到同节点 NVLink 组。

第三步:选优调度

打分后,调度器选分值最高的节点。复杂的场景(如多节点训练)需要协调多个节点的拓扑,这时常配合 Gang Scheduling(见 3.3 节)一起做——把训练的多个 Pod 作为一组同时调度,按整体拓扑最优选择。

3.2.4 K8s 中的拓扑感知方案

K8s 原生调度器对拓扑的支持有限,业界有几种主流方案:

方案 提供者 特点
NVIDIA GPU Operator NVIDIA 自动上报拓扑标签(如 nvidia.com/gpu.product、NUMA 信息),基础支持
Volcano 拓扑感知 华为/CNCF 在 Volcano 调度器中按拓扑打分,支持训练作业
Kueue + 拓扑 Kubernetes SIG Kueue 集成拓扑提示,渐进式增强
Run:AI、Bytedance Volcano fork 第三方 商业或自研增强,支持细粒度拓扑感知
CRD-based 自定义 自研 通过 Device Plugin + 自定义调度器实现

⚠️ 现实挑战:K8s 原生调度器对「拓扑感知」的支持长期是社区痛点。Kubernetes 1.26 引入了 DRA(Dynamic Resource Allocation) 来替代 Device Plugin,目标是更好地表达拓扑与设备组合,但生态成熟仍需时间。生产中多数团队要么用 Volcano/Kueue 内建的拓扑能力,要么自研调度器扩展。

3.2.5 一个端到端例子:8 卡张量并行训练的调度

假设要跑一个 8 卡张量并行(TP=8)的大模型训练,通信极度依赖 NVLink。理想的调度流程:

  1. 声明拓扑偏好:训练 CRD 或 Pod 声明「需要 8 张 NVLink 全互联的 GPU」。
  2. 节点过滤:调度器扫描节点,只保留「单节点有 8 张卡、且 8 张卡全 NVLink 互联」的节点。
  3. 打分选优:在候选节点中按其他维度(负载、网络)打分,选最优。
  4. Gang 调度:把 8 个 worker Pod 同时调度到选中的节点(详见 3.3)。
  5. 拓扑注入:Device Plugin 把 8 张卡按 NVLink 亲和顺序注入容器,确保 worker 0-7 与 GPU 0-7 一一对应。

如果调度失败(比如把 worker 0-3 调度到一个节点、4-7 到另一节点),张量并行跨节点 AllReduce 会让训练慢到不可用。这就是为什么拓扑感知对张量并行训练是「必需品」而非「优化项」。

3.2.6 跨节点拓扑:InfiniBand 与 RoCE

单节点拓扑之外,跨节点的网络拓扑同样关键。多节点分布式训练(数据并行、流水线并行)需要节点间高速网络:

  • InfiniBand(IB):NVIDIA/Mellanox 的高速互联,HDR 单端口 200 Gbps,NDR 400 Gbps,低延迟(微秒级)。
  • RoCE(RDMA over Converged Ethernet):在以太网上跑 RDMA,比 IB 便宜但性能略低。

跨节点通信的关键是 RDMA(Remote Direct Memory Access):让一台节点的 GPU 直接读写另一节点 GPU 的显存,绕过 CPU 与操作系统,极大降低延迟。NCCL(NVIDIA 的集合通信库)会自动选择 NVLink/RDMA 的最快路径。

网络类型 单端口带宽 延迟 成本
千兆以太网 1 Gbps 毫秒级 极低
万兆以太网 10 Gbps 毫秒级
100G 以太网 100 Gbps 微秒级
IB HDR 200 Gbps 微秒级
IB NDR 400 Gbps 亚微秒 极高

💡 判读:跨节点网络是大模型训练的「隐形杀手」。许多团队买了昂贵的 H100,却用 100G 以太网互联,结果 AllReduce 跨节点极慢,整集群吞吐远低于预期。大模型训练几乎必须配 IB 或 RoCE。

3.2.7 拓扑感知之外:拓扑变化的弹性挑战

拓扑在静态部署时是固定的,但弹性训练(详见 3.4)会让拓扑动态变化——加入的新节点可能与原节点不在同一 IB 子网,跨子网通信会变慢。这要求:

  • 弹性感知:训练框架要能识别拓扑变化,调整并行策略(如把张量并行限制在同节点、跨节点用数据并行)。
  • 拓扑提示调度:弹性扩容时调度器优先选「与现有 Pod 同拓扑组」的节点。
  • 降级策略:拓扑恶化时,训练可以选择「继续但变慢」或「等更好拓扑」。

这是当前云原生 AI 的前沿研究方向,业界尚无完美方案,多数团队靠经验规则与手动调优。

本节小结

  • 一个 8 卡节点内部,GPU 之间通过 NVLink、PCIe、跨 NUMA 几种通道互联,带宽差几倍。
  • 张量并行对 NVLink 极度依赖,跨节点张量并行几乎不可行,因此拓扑感知对大模型训练是必需品。
  • 拓扑感知调度的三步:拓扑发现(nvidia-smi、NUMA)→ 分值计算(NV/PIX/PHB/SYS 打分)→ 选优调度。
  • K8s 原生调度器拓扑支持有限,生产中常用 Volcano/Kueue 内建能力或自研扩展,DRA 是未来方向。
  • 跨节点网络用 InfiniBand/RoCE + RDMA,NCCL 自动选择最快路径,是大模型训练的隐形基础设施。
  • 弹性训练带来拓扑动态变化,需要弹性感知与拓扑提示调度,是前沿研究方向。

下一节《3.3 批处理调度器对比:Volcano、Kueue 与 YuniKorn》将讲清 Gang Scheduling 与作业排队的工程实现。


发布者: 作者: 灏天文库 转发
评论区 (0)
U