mpi4py 的 MPI 数据传输错误

Posted

技术标签:

【中文标题】mpi4py 的 MPI 数据传输错误【英文标题】:MPI data transfer errors with mpi4py 【发布时间】:2020-01-18 15:17:18 【问题描述】:

我有一个集群,我被它和 mpi4py 困住了。我有一个相当复杂的代码,而 MPI 只是在传输数据时失败了。 为了让事情更清楚,我编写了一个简单的“hello world”代码,它只是在节点之间传输大型数组。 数组初始化为 0,然后填充来自另一个节点的数组。

import dill
from mpi4py import MPI
MPI.pickle.__init__(dill.dumps, dill.loads)

comm = MPI.COMM_WORLD
rank = comm.rank

import numpy as np

for k in range(5):
    if rank == 0:
        # node 0 sends hi to other nodes
        for i in range(1, comm.size):
            msg = np.ones(10000000, np.double)
            comm.Send([msg, MPI.DOUBLE], dest=i, tag=0)
    else:
        # other nodes receive hi
        msgin = np.zeros(10000000, np.double)
        comm.Recv([msgin, MPI.DOUBLE], source=0, tag=0)
        with open('solution1.txt', 'a') as f:
            f.write(f'rank hi, msgin[:10] np.average(msgin)\n')
        # and then send reply to 0 node
        msgout = np.ones(10000000)
        comm.Send([msgout, MPI.DOUBLE], dest=0, tag=1)

    if rank == 0:
        # node 0 receives replies
        for i in range(1, comm.size):
            msgin = np.zeros(10000000, np.double)
            comm.Recv([msgin, MPI.DOUBLE], tag=1, source=i)
            with open('solution1.txt', 'a') as f:
                f.write(f'rank reply, msgin[:10] np.average(msgin)\n')


结果如下:

1 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
2 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
4 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
5 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
1 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
2 reply [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
4 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
5 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
1 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
2 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
3 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
4 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
5 hi [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 1.0
1 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
2 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
3 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
4 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
5 reply [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08
1 hi [1. 1. 1. 1. 1. 1. 0. 0. 0. 0.] 6e-08

如您所见,有时只传输了 6 个双精度值,而不是 10000000。 此日志不完整 - 所有后续消息也只有 6 个值。 有趣的是,结果是可重现的:节点 2 总是首先回复正确的消息,而所有其他节点都回复不正确的消息。

代码在同一个集群的单个节点上完美运行。它还在谷歌云中完美运行(6 个节点,每个节点 32 个核心)。 我尝试了不同的技巧并得到了相同的结果:

将 Send/Recv 替换为 Isend/Irecv + Wait

将发送/接收与标准泡菜和莳萝泡菜一起使用。此代码在解码泡菜数据时失败。

试过 openmpi 2.1.1、4.0.1 和 intel mpi 库

尝试了英特尔的修复:

export I_MPI_SHM_LMT=shm

可能是网络设置有问题,但我真的不知道该尝试什么。

该设置是一个多节点集群,在 2-1 超额订阅胖树中具有 Mellanox 4x FDR Infiniband 互连。 24 个节点的集合有 12 个上行链路进入大型核心 Infiniband 交换机。每个节点具有 64 GiB 的 4 通道 2133 MHz DDR4 SDRAM(68 GB/秒)内存;两个 Intel Xeon E5-2670 v3 (Haswell) CPU。

【问题讨论】:

回复时,尝试将msgout = np.ones(10000000)替换为msgout = np.ones(10000000, np.double) 谢谢,我已经更正了——但是 numpy 使用 np.double 作为默认值,所以结果是一样的。 【参考方案1】:

在我的设置中,我发现始终使用相同的缓冲区来接收结果很有用。

因此可以尝试在代码的早期声明一次msgin = np.zeros(10000000, np.double) 并一次又一次地使用它。每次收到消息时,您都可以执行msgin.fill(np.nan) 来“清除”缓冲区。

我不知道为什么它对我有用,但问题似乎消失了。

祝你好运!

【讨论】:

【参考方案2】:

我在类似的集群环境中遇到了类似的问题。它仅出现在与 Infiniband 多节点通信相关的情况下,用于特定大小 (> 30MB) 的发送/接收缓冲区。我可以通过从 OpenMPI 3.x 降级到 OpenMPI 2.1.5 来解决问题(Mpi4py 版本似乎无关紧要)

相应的 C 代码运行良好(相同的集群、内核/节点数、相同的 OpenMPI 3.x 等)因此问题似乎在 mpi4py 和 OpenMPI 3 之间,但我无法调试。

【讨论】:

我还发现,当缓冲区大小很小(在我的情况下为 参数是 I_MPI_FABRICS=shm:tcp 在这两种情况下,传输速率都没有接近规格(大约 100Mb/s 与多个 GB/s)我无法进一步调试它,这是一个限制访问的竞争平台。比赛已经结束了,反正集群也要退役了。

以上是关于mpi4py 的 MPI 数据传输错误的主要内容,如果未能解决你的问题,请参考以下文章

mpi4py:空闲内核显着放缓

大数据系列9:mpi4py高性能计算

mpi4py - 使用矩阵列的类型

让 Pycuda 在 2 gpus 上使用 Mpi4py [关闭]

MPI4PY大阵列散射产生死锁

通过 MPI [MPI4py] 发送复数时的 MPI_ERR_TRUNCATE