11.2.2. TCP

错误

待办事项 本节需要从常见问答(FAQ)风格转换为常规文档风格。

11.2.2.1. 如何指定使用IP网络传输MPI消息?

Open MPI 通常在以下情况下会自动使用 tcp BTL:

  1. tcp BTL在运行时可用(在大多数类POSIX系统上应该如此),并且

  2. 没有可用的高性能网络

当使用tcp BTL时,通常也会(自动)同时使用selfsm BTL,分别用于进程内回环和节点内回环通信。

如果想确保使用tcpsmself这些BTL组件,可以在mpirun命令行中显式指定它们:

shell$ mpirun --mca pml ob1 --mca btl tcp,sm,self ...

警告

如果未指定sm BTL,当Open MPI使用TCP网络堆栈向同一主机上的对等节点发送数据时,可能会导致性能下降。

警告

如果未指定self BTL,可能导致Open MPI无法完成自发送场景(这意味着您的程序在进程尝试向自身发送数据之前都能正常运行)。


11.2.2.2. 等等——我使用的是高速网络。必须禁用TCP BTL吗?

不。遵循所谓的"最小惊讶原则",Open MPI假设如果您同时拥有IP网络和至少一个高速网络(如InfiniBand),您可能只想使用高速网络进行MPI消息传递。因此,tcpBTL组件会感知到这一点并自动停用自身。

尽管如此,Open MPI可能仍会使用TCP进行设置和拆除信息的传输——因此您会在MPI作业启动和关闭期间看到IP网络上的流量。这是正常现象,不会影响MPI消息传递通道。


11.2.2.3. 如何知道有哪些MCA参数可用于调优MPI性能?

ompi_info 命令可以显示 tcp BTL 组件(即使用 TCP 进行 MPI 通信的组件)的所有可用参数:

shell$ ompi_info --param btl tcp --level 9

11.2.2.4. Open MPI是否使用IP回环接口?

通常不会。

在一般的消息传递应用中,有两种场景可能会使用操作系统的IP回环接口:

  1. 从一个进程向自身发送消息

  2. 在同一台机器上从一个进程向另一个进程发送消息

TCP BTL 在 Open MPI 中不处理"自我发送"场景;事实上,它甚至不具备这种能力。相反,self BTL 组件用于所有自我发送的 MPI 通信。这不仅让所有 Open MPI BTL 组件避免了针对自我发送场景的特殊情况代码,还能避免使用低效的回环网络栈(如 IP 回环设备)。

具体来说:self组件在自发送场景中使用其自有机制;它不使用操作系统网络接口(如IP环回接口)。

当向同一台机器上的其他进程发送消息时,Open MPI默认会使用共享内存BTL(sm)。如果用户停用了这些BTL组件,根据其他可用BTL组件的情况,可能会选择TCP BTL用于同一节点上进程间的消息传递,这种情况下很可能会使用IP回环设备。但这不是默认行为;除非共享内存无法正常启动,或者用户明确要求不使用共享内存BTL。


11.2.2.5. 我的集群部分/全部节点上有多个IP网络。Open MPI会使用哪些?

通常情况下,Open MPI会贪婪地使用其可达性计算发现的所有IP网络。

要更改此行为,您可以选择性地包含特定网络或排除特定网络。查看此常见问题条目以获取更多详细信息。


我遇到了与TCP相关的错误。这些错误是什么意思?

与TCP相关的错误通常由Open MPI以类似以下消息的形式报告:

btl_tcp_endpoint.c:572:mca_btl_tcp_endpoint_complete_connect: connect() failed with errno=113
mca_btl_tcp_frag_send: writev failed with errno=104

如果显示了一个errno错误号但没有解释字符串,您可以在您的操作系统上查看该特定错误号的含义。在Linux系统上,您可以使用perror命令:

# See what errno 113 is
shell$ perror 113
OS error code 113:  No route to host

# See what errno 104 is
shell$ perror 104
OS error code 104:  Connection reset by peer

通常向Open MPI用户邮件列表报告的两类错误是:

  1. 无法连接到主机: 这类错误通常意味着存在多个IP接口可用,但它们不符合Open MPI对路由可达性的假设。更多信息请参阅TCP路由可达性假设FAQ条目TCP选择FAQ条目

  2. 对端连接重置: 这类错误通常发生在MPI_INIT完成后,一般表明某个MPI进程意外终止(例如由于段错误等严重错误)。具体的错误信息表明某个对等MPI进程尝试向已终止的MPI进程写入数据但失败了。


11.2.2.6. 如何指定Open MPI使用哪些IP接口/网络?

在某些高性能计算(HPC)环境中,每个节点配置多个IP接口的情况并不罕见。例如,一个IP网络可能是"低速"的,用于批处理调度器、网络文件系统和/或交互式登录等控制信息传输。而另一个(或多个)IP网络可能是"高速"的,专门用于并行应用程序在运行期间使用。再举一个例子,某些操作系统还可能配置虚拟接口用于与虚拟机通信。

除非另有说明,Open MPI会贪婪地使用所有可用的"启用"IP网络,并按需尝试连接到所有对等节点(即Open MPI不会在MPI_INIT期间为所有MPI对等节点打开套接字——详见此FAQ条目)。因此,如果您希望MPI作业不使用特定的IP网络——或完全不使用任何IP网络——那么您需要明确告知Open MPI。

警告

在某些情况下,过度使用所有"启用"状态的网络接口可能会引发问题。例如,如果您的机器配有仅限本地使用的接口(如回环设备,或仅能在该机器上使用的虚拟机桥接设备,无法用于与其他机器上的MPI进程通信),您可能需要告知Open MPI忽略这些网络。

Open MPI 默认情况下通常会忽略回环设备,但其他仅限本地的设备必须手动忽略。 用户曾报告过 RHEL6 自动安装virbr0设备用于Xen虚拟化的情况。 该接口被自动分配了192.168.1.0/24子网的IP地址并被标记为"up"状态。由于Open MPI在所有节点的所有MPI进程中都能看到这个处于"up"状态的192.168.1.0/24接口, 它误认为该网络可用于MPI通信。这显然是错误的,并导致MPI应用程序在尝试发送或接收MPI消息时挂起。

  1. 要禁用Open MPI使用TCP进行MPI通信,应相应设置tcp MCA参数。您可以选择排除TCP组件或包含所有其他组件。具体如下:

    # 这表示排除TCP BTL组件
    # (隐式包含所有其他组件)
    shell$ mpirun --mca btl ^tcp...
    
    # 这表示仅包含列出的BTL组件
    # (tcp未被列出,因此不会被使用)
    shell$ mpirun --mca btl self,vader,openib ...
    
  2. 如果您想使用TCP进行MPI通信,但希望限制某些网络的使用,可以使用btl_tcp_if_includebtl_tcp_if_exclude MCA参数(两者只需设置其中一个)。这些参数的值可以是逗号分隔的网络接口列表。例如:

    # 这表示不使用eth0和lo接口。
    # (隐式使用所有其他接口)。根据上面的描述,
    # 如果指定了排除列表,则必须包含IP回环和所有仅本地设备。
    shell$ mpirun --mca btl_tcp_if_exclude lo,eth0 ...
    
    # 这表示仅使用eth1和eth2接口
    # (隐式忽略其他接口)
    shell$ mpirun --mca btl_tcp_if_include eth1,eth2 ...
    
  3. 您也可以在包含或排除列表中使用CIDR表示法指定子网。例如:

    # 仅使用192.168.1.0/24和10.10.0.0/16子网进行MPI通信:
    # communications:
    shell$ mpirun --mca btl_tcp_if_include 192.168.1.0/24,10.10.0.0/16 ...
    

    注意

    您必须精确指定给定网络的CIDR表示法。例如,如果您有两个IP网络10.10.0.0/24和10.10.1.0/24,当您指定"10.10.0.0/16"时,Open MPI将无法识别其中任何一个。

警告

如果您使用btl_tcp_if_includebtl_tcp_if_exclude MCA参数来配置TCP BTL在MPI通信中的行为, 您可能还需要研究对应的MCA参数 oob_tcp_if_includeoob_tcp_if_exclude,这些参数用于配置非MPI的 基于TCP的通信(例如在MPI_INITMPI_FINALIZE期间 的通信设置和协调)。

错误

TODO PMIx中是否还存在对应的OOB TCP参数?

请注意,Open MPI仍会使用TCP传输控制消息,例如mpirun与MPI进程之间的数据交换、MPI_INIT期间的会合信息等。若要完全禁用TCP,您还需要在OOB框架中禁用tcp组件。

错误

待办事项 这在PMIx中可行吗?我表示怀疑…?


11.2.2.7. Open MPI 在 MPI_INIT 期间会打开大量套接字吗?

尽管Open MPI在MPI_INIT期间可能会打开多个TCP套接字,但tcp BTL组件并不会在MPI_INIT期间为每个MPI对等进程都打开一个套接字。 Open MPI会根据需要打开套接字——因此当进程首次向对等方发送消息且两者之间没有TCP连接时,Open MPI会自动建立新的套接字连接。

因此,如果你的并行应用在节点间通信较为稀疏,运行大量进程时(例如耗尽每个进程的文件描述符)应该不会遇到扩展性问题。


11.2.2.8. 是否有任何Linux内核TCP参数需要设置?

对此问题,大家的看法各不相同,同时也取决于您具体的硬件和环境。以下是一些用户认为有帮助的通用指导原则。

  1. net.ipv4.tcp_syn_retries: 某些Linux系统具有非常大的初始连接超时时间——它们会多次重试发送SYN数据包,然后才确定无法建立连接。如果MPI无法建立套接字连接,最好让它们相对快速地失败(几分钟而不是几小时)。您可能需要将此值减小;效果可能因人而异。

  2. net.ipv4.tcp_keepalive_time: 某些MPI应用会先通过TCP发送一波初始MPI消息,然后长时间保持静默(例如易并行应用)。Linux可能会因长时间未检测到这些休眠TCP套接字上的活动而判定其失效。因此您可能需要延长TCP不活动超时时间。许多Linux系统默认设置为7,200秒;必要时可增加该值。

  3. Increase TCP buffering for 10G or 40G Ethernet. Many Linux distributions come with good buffering presets for 1G Ethernet. In a datacenter/HPC cluster with 10G or 40G Ethernet NICs, this amount of kernel buffering is typically insufficient. Here’s a set of parameters that some have used for good 10G/40G TCP bandwidth:

    • net.core.rmem_max: 16777216

    • net.core.wmem_max: 16777216

    • net.ipv4.tcp_rmem: 4096 87380 16777216

    • net.ipv4.tcp_wmem: 4096 65536 16777216

    • net.core.netdev_max_backlog: 30000

    • net.core.rmem_default: 16777216

    • net.core.wmem_default: 16777216

    • net.ipv4.tcp_mem: '16777216 16777216 16777216'

    • net.ipv4.route.flush: 1

    上述每一项都是Linux内核参数,可以通过多种不同方式进行设置。

    1. 您可以通过/proc文件系统更改运行中的内核参数:

      shell# cat /proc/sys/net/ipv4/tcp_syn_retries
      5
      shell# echo 6 > /proc/sys/net/ipv4/tcp_syn_retries
      
    2. 您也可以使用sysctl命令:

      shell# sysctl net.ipv4.tcp_syn_retries
      net.ipv4.tcp_syn_retries = 5
      shell# sysctl -w net.ipv4.tcp_syn_retries=6
      net.ipv4.tcp_syn_retries = 6
      
    3. 或者您可以通过在/etc/sysctl.conf中添加条目来设置它们,这些设置在重启后仍然有效:

      shell$ grep tcp_syn_retries /etc/sysctl.conf
      net.ipv4.tcp_syn_retries = 6
      
    4. 您的Linux发行版可能也支持将单个文件放在 /etc/sysctl.d目录下(即使该目录尚不存在), 这实际上比将它们放在 /etc/sysctl.conf中是更好的做法。例如:

      shell$ cat /etc/sysctl.d/my-tcp-settings
      net.ipv4.tcp_syn_retries = 6
      

11.2.2.9. Open MPI如何知道哪些IP地址可以互相路由?

Open MPI 假设只要接口属于相同的地址族(IPv4或IPv6),所有接口都是可路由的。我们运用图论原理,根据连接质量给每条可能的连接赋予权重值。这使得库函数能够选择节点之间的最优连接路径。该方法还支持链路聚合,但会确保任何接口不会建立多条连接。

连接质量的定义如下,数值越高表示连接质量越好。请注意,当为包含私有地址和公共地址的连接分配权重时,系统会赋予其PRIVATE_DIFFERENT_NETWORK的权重值。

            NO_CONNECTION = 0
PRIVATE_DIFFERENT_NETWORK = 1
PRIVATE_SAME_NETWORK      = 2
PUBLIC_DIFFERENT_NETWORK  = 3
PUBLIC_SAME_NETWORK       = 4

通过一个示例可以很好地说明两个不同节点上的进程如何建立连接。这里我们有两个节点,每个节点都有多种接口:

       Node A                Node B
 ----------------       ----------------
|      lo0       |     |      lo0       |
| 127.0.0.1/8    |     | 127.0.0.1/8    |
|                |     |                |
|      eth0      |     |      eth0      |
| 10.8.47.1/24   |     | 10.8.47.2/24   |
|                |     |                |
|      eth1      |     |      eth1      |
| 192.168.1.1/24 |     | 192.168.1.2/24 |
|                |     |                |
|      eth2      |     |                |
| 192.168.2.2/24 |     |                |
 ----------------      ------------------

基于这两个节点,软件构建了一个二分图,展示了所有可能的连接及其对应的权重。默认情况下,lo0接口会被排除,因为MCA参数btl_tcp_if_exclude默认设置为lo。以下是所有可能的连接及其权重的示意图。

      Node A       Node B
eth0 --------- 2 -------- eth0
       ------- 1 -------- eth1

eth1 --------- 1 -------- eth0
       ------- 2 -------- eth1

eth2 --------- 1 -------- eth0
       ------- 1 -------- eth1

随后,该库会检查所有连接并选择最优的。这样就在两个节点之间建立了两条连接。

如果您对进程实际进行的connect()调用感到好奇,可以运行--mca btl_base_verbose 30参数。当您发现作业卡住并怀疑可能是库正在尝试连接到不可达主机时,这个功能会很有帮助。

# Here is an example with some of the output deleted for clarity.
# One can see the connections that are attempted.
shell$ mpirun --mca btl self,sm,tcp --mca btl_base_verbose 30 -n 2 -host NodeA,NodeB a.out
[...snip...]
[NodeA:18003] btl: tcp: attempting to connect() to address 10.8.47.2 on port 59822
[NodeA:18003] btl: tcp: attempting to connect() to address 192.168.1.2 on port 59822
[NodeB:16842] btl: tcp: attempting to connect() to address 192.168.1.1 on port 44500
[...snip...]

如果您想了解更多关于连接代码背后的理论细节,可以在这篇IEEE论文中找到背景故事。


11.2.2.10. Open MPI 是否会关闭 TCP 套接字?

一般来说,不需要。

尽管TCP套接字是"惰性"打开的(这意味着MPI连接/TCP套接字仅在需要时才会建立——而不是在MPI_INIT期间打开所有MPI对等进程之间可能的套接字),但它们永远不会被关闭。


11.2.2.11. Open MPI是否支持拥有多个IP地址的IP接口?

一般来说,不需要。

例如,如果您的ifconfig输出显示单个IP设备具有多个IP地址,如下所示:

0: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
   link/ether 00:18:ae:f4:d2:29 brd ff:ff:ff:ff:ff:ff
   inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0:1
   inet 10.10.0.3/24 brf 10.10.0.255 scope global eth0
   inet6 fe80::218:aef2:29b4:2c4/64 scope link
      valid_lft forever preferred_lft forever

(注意这里有两行inet)

那么Open MPI将无法使用此设备。


11.2.2.12. Open MPI是否支持虚拟IP接口?

编号。

例如,如果你的ifconfig输出同时包含eth0eth0:0,当你使用TCP BTL时,Open MPI可能会混淆, 导致挂起或其他不可预测的行为。

请注意,使用btl_tcp_if_includebtl_tcp_if_exclude来避免使用虚拟接口并不能解决该问题。


11.2.2.13. 是否可以使用多个TCP连接来提升网络性能?

Open MPI可以在任意一对MPI进程之间使用多条TCP连接,将大消息分散传输到这些连接上。参数btl_tcp_links可用于设置MPI进程之间应建立的TCP连接数量。

需要注意的是,当每个主机上运行大量MPI进程时,这种优化可能无法提升近邻交换这类常见应用场景的性能。因为在这种情况下,任意两台主机之间已经存在大量TCP连接(由于众多进程都在进行通信),额外的TCP连接很可能只会消耗更多资源,并增加MPI实现的处理负担。

然而,对于高度多线程的应用程序,当每个主机上仅运行一两个MPI进程时,btl_tcp_links选项可能会显著提升TCP吞吐量。