3、硬件环境摸底:GPU拓扑结构(NVLink、PCIe)、CPU与内存带宽、网络互联(IB/RoCE)

做分布式训练,最怕什么?

我最怕的是:代码写好了,集群跑起来了,结果性能只有理论值的30%。

你查日志、调参数、改代码,折腾两天,最后发现——是硬件拓扑没摸清。GPU之间走的是PCIe而不是NVLink,跨节点通信走了慢路径。嗯,这种坑我踩过不止一次。

所以这一章,咱们就聊聊怎么给硬件环境「摸底」。说白了,就是搞清楚你的机器到底长什么样,GPU怎么连的,CPU和内存带宽够不够,网络是不是瓶颈。

3.1 GPU拓扑结构:NVLink vs PCIe

先问个问题:你机器里有8张卡,它们之间怎么通信?

答案取决于拓扑。我见过不少同学,上来就调大batch size,结果发现通信开销比计算还大。为什么?因为GPU之间走的是PCIe,带宽只有NVLink的十分之一。

3.1.1 用 nvidia-smi 看拓扑

最直接的工具就是 nvidia-smi。但大多数人只用它看显存和温度,其实它还能看拓扑。

# 查看GPU之间的连接关系
nvidia-smi topo -m

输出大概长这样:

        GPU0    GPU1    GPU2    GPU3    GPU4    GPU5    GPU6    GPU7
GPU0     X      NV2     NV2     NV2     PHB     PHB     PHB     PHB
GPU1    NV2      X      NV2     NV2     PHB     PHB     PHB     PHB
GPU2    NV2     NV2      X      NV2     PHB     PHB     PHB     PHB
GPU3    NV2     NV2     NV2      X      PHB     PHB     PHB     PHB
GPU4    PHB     PHB     PHB     PHB      X      NV2     NV2     NV2
GPU5    PHB     PHB     PHB     PHB     NV2      X      NV2     NV2
GPU6    PHB     PHB     PHB     PHB     NV2     NV2      X      NV2
GPU7    PHB     PHB     PHB     PHB     NV2     NV2     NV2      X

这里 NV2 表示NVLink 2.0连接,PHB 表示走PCIe。你看,GPU0-3之间是NVLink,GPU4-7之间也是NVLink,但两组之间只能走PCIe。

关键点:分布式训练中,AllReduce通信尽量在NVLink域内完成。跨域通信会经过PCIe,带宽骤降。

3.1.2 我踩过的坑

有一次,我在一台8卡机器上跑模型并行。我把模型切了两半,分别放在GPU0和GPU4上。结果每次前向传播都要等很久。

查了半天,才发现GPU0和GPU4之间走的是PCIe。我赶紧把模型切到GPU0和GPU1上,速度直接翻倍。

所以我的习惯是:跑任何分布式任务之前,先跑一遍 nvidia-smi topo -m,把拓扑图画出来。

3.2 CPU与内存带宽

GPU再快,数据也得从CPU内存搬过去。如果CPU内存带宽不够,GPU就得干等着。

3.2.1 用 lstopo 看CPU拓扑

lstopo 是hwloc工具包里的一个命令。它能画出CPU的NUMA节点、缓存层级、内存控制器位置。

# 安装hwloc
sudo apt-get install hwloc

# 查看CPU拓扑
lstopo

输出是一张图(或者文本)。你会看到:

  • 有几个NUMA节点
  • 每个NUMA节点下有哪些CPU核心
  • 内存控制器挂在哪个节点上
  • PCIe设备(包括GPU)连在哪个节点上

小技巧:如果GPU和CPU不在同一个NUMA节点,数据拷贝会有额外延迟。我一般用 numactl --membind 把进程绑定到GPU所在的NUMA节点。

3.2.2 实测内存带宽

光看拓扑不够,还得测实际带宽。我常用 stream 基准测试:

# 下载编译stream
wget https://www.cs.virginia.edu/stream/FTP/Code/stream.c
gcc -O3 -fopenmp -DSTREAM_ARRAY_SIZE=200000000 -DNTIMES=20 stream.c -o stream

# 运行
./stream

输出会告诉你:

  • Copy带宽
  • Scale带宽
  • Add带宽
  • Triad带宽

我遇到过一台机器,理论带宽是200GB/s,实测只有120GB/s。查了半天,发现是BIOS里内存频率没配好。嗯,这种硬件配置问题,工具一测就现原形。

3.3 网络互联:IB vs RoCE

跨节点训练,网络是命门。你想想看,8台机器,每台8卡,一共64张卡做AllReduce。如果网络带宽不够,通信时间比计算时间还长。

3.3.1 用 ibstatus 看IB网络

如果你的集群用了InfiniBand:

# 查看IB设备状态
ibstatus

输出:

Infiniband device 'mlx5_0' port 1 status:
    state: ACTIVE
    physical state: LINK_UP
    rate: 200 Gb/sec (4X HDR)

这里 rate: 200 Gb/sec 就是单端口带宽。HDR是200Gbps,EDR是100Gbps,FDR是56Gbps。

3.3.2 用 ethtool 看RoCE

如果用的是RoCE(RDMA over Converged Ethernet):

# 查看网卡速率
ethtool enp175s0 | grep Speed

# 输出
Speed: 100000Mb/s

100000Mb/s = 100Gbps。但注意,这是链路层速率,实际有效带宽要低一些。

注意:RoCE对网络质量要求很高。我曾经遇到丢包率0.1%,结果训练速度直接腰斩。所以用RoCE之前,一定要测一下 iperf 和 ib_write_bw。

3.3.3 实测网络带宽

我习惯用 ib_write_bw 测点对点带宽:

# 在服务端
ib_write_bw -d mlx5_0

# 在客户端
ib_write_bw -d mlx5_0 192.168.1.1

输出会显示不同消息大小下的带宽。重点关注大消息(1MB以上)的带宽,因为分布式训练中梯度同步都是大消息。

3.4 一张图总结:硬件摸底流程

下面这张图,是我自己总结的硬件摸底流程。每次拿到新集群,我都会按这个步骤走一遍。

硬件摸底流程 1. GPU拓扑摸底 nvidia-smi topo -m 识别NVLink域 标记PCIe跨域 2. CPU/内存摸底 lstopo 看NUMA拓扑 stream 测内存带宽 numactl 绑定NUMA 3. 网络互联摸底 ibstatus / ethtool ib_write_bw 实测 iperf 测RoCE

3.5 实战建议

最后,给你几个我自己的实战建议:

  1. 先摸底,再调参。我见过太多人上来就调学习率、调batch size,结果硬件瓶颈都没发现。
  2. 把摸底结果写下来。我习惯在每台机器上跑一遍 nvidia-smi topo -m 和 lstopo,把结果截图保存。这样出问题时能快速定位。
  3. 网络带宽要留余量。理论带宽和实际带宽之间,通常有20%-30%的差距。别把理论值当上限。
  4. 跨节点通信,优先走IB。如果集群同时有IB和RoCE,IB的稳定性和延迟都更好。我踩过RoCE的坑,丢包率一高,训练直接崩。

一句话总结:硬件摸底不是一次性的工作。每次集群变更、每次新任务启动,都值得重新摸底。你想想看,花10分钟摸底,可能省下你一天调参的时间。

想要更多资料,联系可可