Horovod 与 MPI¶
MPI 可作为 Gloo 的替代方案,用于协调 Horovod 中进程之间的工作。使用 NCCL 时,两者的性能相似,但如果您进行 CPU 训练,使用 MPI 会带来显著的性能优势。
首先安装 Open MPI 或其他 MPI 实现。了解如何安装 Open MPI 在此页面。
注意: Open MPI 3.1.3 存在一个可能导致挂起的问题。推荐的解决方法是降级到 Open MPI 3.1.2 或升级到 Open MPI 4.0.0。
mpirun¶
horovodrun 提供了一个基于 Open MPI 的便捷包装器,用于运行 Horovod 脚本。
在某些情况下,需要精细控制传递给 Open MPI 的选项。本页介绍直接使用 Open MPI 运行 Horovod 训练。
在拥有4个GPU的机器上运行:
horovodrun -np 4 python train.py
等效的Open MPI命令:
mpirun -np 4 \ -bind-to none -map-by slot \ -x NCCL_DEBUG=INFO -x LD_LIBRARY_PATH -x PATH \ -mca pml ob1 -mca btl ^openib \ python train.py
在4台机器上运行,每台机器有4个GPU:
horovodrun -np 16 -H server1:4,server2:4,server3:4,server4:4 python train.py
等效的Open MPI命令:
mpirun -np 16 \ -H server1:4,server2:4,server3:4,server4:4 \ -bind-to none -map-by slot \ -x NCCL_DEBUG=INFO -x LD_LIBRARY_PATH -x PATH \ -mca pml ob1 -mca btl ^openib \ python train.py
从 Open MPI 3 开始,添加 -bind-to none 和 -map-by slot 参数非常重要。
-bind-to none 指定 Open MPI 不将训练进程绑定到单个 CPU 核心(这会损害性能)。
-map-by slot 允许您混合不同的 NUMA 配置,因为默认行为是绑定到插槽。
-mca pml ob1 和 -mca btl ^openib 标志强制使用 TCP 进行 MPI 通信。这避免了 Open MPI 在使用 RDMA 时常见的多进程问题,这些问题通常会导致段错误。使用 TCP 进行 MPI 通信不会对性能产生明显影响,因为大部分繁重的通信由 NCCL 处理,如果可用,NCCL 将通过 RoCE 或 InfiniBand 使用 RDMA(参见 Horovod on GPU)。此规则的显著例外是大量使用 hvd.broadcast() 和 hvd.allgather() 操作的模型。要使这些操作使用 RDMA,请阅读下面的 Open MPI with RDMA 部分。
使用 -x 选项,您可以指定 (-x NCCL_DEBUG=INFO) 或复制 (-x LD_LIBRARY_PATH) 环境变量到所有工作节点。
自定义SSH端口¶
使用 -mca plm_rsh_args "-p 指定自定义 SSH 端口,如下所示:
mpirun -np 16 \
-H server1:4,server2:4,server3:4,server4:4 \
-bind-to none -map-by slot \
-mca plm_rsh_args "-p 12345"
-x NCCL_DEBUG=INFO -x LD_LIBRARY_PATH -x PATH \
-mca pml ob1 -mca btl ^openib \
python train.py
这在Docker环境中运行Horovod的情况下经常很有用。
使用RDMA的Open MPI¶
如上所述,在大多数情况下,使用TCP进行MPI通信对性能没有显著影响。大量使用hvd.broadcast()和hvd.allgather()操作的模型是该规则的例外情况。
默认的 Open MPI openib BTL 提供 RDMA 功能,但在 MPI 多线程环境下表现不佳。为了在使用 openib 时启用 RDMA,必须通过 -x HOROVOD_MPI_THREADS_DISABLE=1 选项禁用多线程。请参考以下示例:
mpirun -np 16 \
-H server1:4,server2:4,server3:4,server4:4 \
-bind-to none -map-by slot \
-x NCCL_DEBUG=INFO -x LD_LIBRARY_PATH -x HOROVOD_MPI_THREADS_DISABLE=1 -x PATH \
-mca pml ob1 \
python train.py
其他MPI RDMA实现可能从禁用多线程中受益,也可能不会,因此请查阅供应商文档。
Horovod 参数旋钮¶
许多可作为命令行参数提供给 horovodrun 的可配置参数,可以通过使用环境变量与 mpirun 一起使用。
张量融合:
$ mpirun -x HOROVOD_FUSION_THRESHOLD=33554432 -x HOROVOD_CYCLE_TIME=3.5 ... python train.py
时间线:
$ mpirun -x HOROVOD_TIMELINE=/path/to/timeline.json -x HOROVOD_TIMELINE_MARK_CYCLES=1 ... python train.py
自动调优:
$ mpirun -x HOROVOD_AUTOTUNE=1 -x HOROVOD_AUTOTUNE_LOG=/tmp/autotune_log.csv ... python train.py
请注意,当使用horovodrun时,任何命令行参数都将覆盖在环境中设置的值。
由于未路由的网络接口导致挂起¶
未路由的网络接口可能导致Open MPI挂起。此类接口的一个例子是docker0。
如果在 ifconfig 的输出中看到非路由接口(如 docker0),你应该通过 -mca btl_tcp_if_exclude 和 NCCL_SOCKET_IFNAME=^ 参数告诉 Open MPI 和 NCCL 不要使用它们。
ifconfig
产生类似这样的输出:
docker0 Link encap:Ethernet HWaddr 02:42:2d:17:ea:66
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
eth0 Link encap:Ethernet HWaddr 24:8a:07:b3:7d:8b
inet addr:10.0.0.1 Bcast:10.0.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:900002410 errors:0 dropped:405 overruns:0 frame:0
TX packets:1521598641 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:376184431726 (350.3 GiB) TX bytes:954933846124 (889.3 GiB)
eth1 Link encap:Ethernet HWaddr 24:8a:07:b3:7d:8a
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2410141 errors:0 dropped:0 overruns:0 frame:0
TX packets:2312177 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:698398061 (666.0 MiB) TX bytes:458504418 (437.2 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:497075633 errors:0 dropped:0 overruns:0 frame:0
TX packets:497075633 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:72680421398 (67.6 GiB) TX bytes:72680421398 (67.6 GiB)
示例 mpirun 命令,排除 lo 和 docker0 接口:
mpirun -np 16 \
-H server1:4,server2:4,server3:4,server4:4 \
-bind-to none -map-by slot \
-x NCCL_DEBUG=INFO -x LD_LIBRARY_PATH -x PATH \
-x NCCL_SOCKET_IFNAME=^lo,docker0 \
-mca pml ob1 -mca btl ^openib \
-mca btl_tcp_if_exclude lo,docker0 \
python train.py