MPI_SEND占用了大量的虚拟内存

在大量的内核上debugging我的程序时,我遇到了insufficient virtual memory非常奇怪的错误。 我的调查导致代码的和平,主人向每个奴隶发送小消息。 然后我写了一个小程序,其中1个主站只发送10个整数, MPI_SEND ,所有从站都接收到MPI_RECV 。 比较MPI_SEND之前和之后的文件/proc/self/status ,内存大小之间的差异是巨大的! 最有趣的事情(这使我的程序崩溃),是这个内存不会释放MPI_Send后,仍然占用巨大的空间。

有任何想法吗?

  System memory usage before MPI_Send, rank: 0 Name: test_send_size State: R (running) Pid: 7825 Groups: 2840 VmPeak: 251400 kB VmSize: 186628 kB VmLck: 72 kB VmHWM: 4068 kB VmRSS: 4068 kB VmData: 71076 kB VmStk: 92 kB VmExe: 604 kB VmLib: 6588 kB VmPTE: 148 kB VmSwap: 0 kB Threads: 3 System memory usage after MPI_Send, rank 0 Name: test_send_size State: R (running) Pid: 7825 Groups: 2840 VmPeak: 456880 kB VmSize: 456872 kB VmLck: 257884 kB VmHWM: 274612 kB VmRSS: 274612 kB VmData: 341320 kB VmStk: 92 kB VmExe: 604 kB VmLib: 6588 kB VmPTE: 676 kB VmSwap: 0 kB Threads: 3 

Solutions Collecting From Web of "MPI_SEND占用了大量的虚拟内存"

这是几乎所有在InfiniBand上运行的MPI实现的预期行为。 IB RDMA机制要求数据缓冲区应该被注册,也就是说,它们首先被锁定在物理内存中的固定位置,然后驱动程序告诉InfiniBand HCA如何将虚拟地址映射到物理内存。 这是非常复杂的,因此注册内存以供IB HCA使用的过程非常缓慢 ,这就是为什么大多数MPI实现从不注销曾经注册过的内存,希望以后同样的内存将被用作源或数据目标。 如果注册的内存是堆内存,它永远不会返回到操作系统,这就是为什么你的数据段只增长大小。

重复使用发送和接收缓冲区尽可能多。 请记住,InfiniBand上的通信会导致高内存开销。 大多数人并没有真正想到这个问题,而且通常记录不完善,但是InfiniBand使用了很多特殊的数据结构(队列),这些结构被分配到进程的内存中,这些队列随着进程的数量而显着增长。 在一些完全连接的情况下,队列内存的数量可能非常大,实际上没有为应用程序留下内存。

有一些参数可以控制Intel MPI使用的IB队列。 在你的情况最重要的是I_MPI_DAPL_BUFFER_NUM它控制预先分配和预先注册的内存量。 它的默认值是16 ,所以你可能想减少它。 请注意可能的性能影响。 您也可以尝试通过将I_MPI_DAPL_BUFFER_ENLARGEMENT设置为1来使用动态预分配的缓冲区大小。 启用此选项后,英特尔MPI将首先注册小缓冲区,并在稍后根据需要进行扩展。 还要注意,IMPI懒惰地打开连接,这就是为什么你只有在调用MPI_Send之后才能看到已用内存的巨大增加。

如果不使用DAPL运输,例如使用运输方式,那么你可以做的事情就不多了。 您可以通过将I_MPI_OFA_USE_XRC设置为1来启用XRC队列。 这应该以某种方式减少使用的内存。 如果程序的通信图没有完全连接(一个完全连接的程序是其中每个等级与所有其他等级对话的程序),则通过将I_MPI_OFA_DYNAMIC_QPS设置为1来启用动态队列对创建可能减少存储器使用。

Hristo的回答大部分是正确的,但是由于你使用的是小信息,所以有一点区别。 消息最终在急切的路径上:它们首先被复制到一个已经注册的缓冲区,然后该缓冲区被用于传输,而接收者将消息从它们末端的预留缓冲区中复制出来。 在您的代码中重复使用缓冲区只会帮助处理大量的消息。

这样做是为了避免注册用户提供的缓冲区的速度缓慢。 对于较大的消息,副本比注册需要更长的时间,所以使用会合协议。

这些渴望的缓冲区有点浪费。 例如,默认情况下,它们在英特尔MPI上使用OF动词时为16kB。 除非使用消息聚合,否则每个10-int大小的消息正在吃掉4个4kB的页面。 但是,聚合无论如何也不会帮助多个接收者。

那么该怎么办? 减少渴望缓冲区的大小。 这是通过设置热切/汇合门限( I_MPI_RDMA_EAGER_THRESHOLD环境变量)来控制的。 尝试2048或更小。 请注意,这可能会导致延迟增加。 或者更改I_MPI_DAPL_BUFFER_NUM变量来控制这些缓冲区的数量,或者尝试Hristo建议的动态调整大小功能。 这假定您的IMPI正在使用DAPL(默认)。 如果直接使用OF动词,则DAPL变量将不起作用。


编辑:所以得到这个运行的最终解决方案是设置I_MPI_DAPL_UD=enable 。 我可以推测这个魔法的起源,但是我没有获得英特尔的代码来确认这一点。

IB可以有不同的传输模式,其中两个是RC(可靠连接)和UD(不可靠数据报)。 RC需要主机之间的明确连接(如TCP),并且每个连接都花费一些内存。 更重要的是,每个连接都有与之相关的渴望缓冲区,这真的是相加的。 这是您使用英特尔的默认设置。

有一个优化可能:共享连接之间的渴望缓冲区(这称为SRQ – 共享接收队列)。 还有一个名为XRC(扩展RC)的进一步Mellanox扩展,进一步进行队列共享:在同一节点上的进程之间。 默认情况下,英特尔的MPI通过DAPL访问IB设备,而不是直接通过OF动词访问。 我的猜测是这排除了这些优化(我没有DAPL的经验)。 可以通过设置I_MPI_FABRICS=shm:ofa I_MPI_OFA_USE_XRC=1I_MPI_OFA_USE_XRC=1 (使Intel MPI使用OFA接口而不是DAPL)来启用XRC支持。

当您切换到UD传输时,您可以在缓冲区共享上进一步优化:不再需要跟踪连接。 缓冲区共享在这个模型中是很自然的:因为没有连接,所有内部缓冲区都在一个共享池中,就像SRQ一样。 所以还有更多的内存节省,但代价是:数据报传输可能会失败,这取决于软件,而不是IB硬件来处理重传。 当然,这对于使用MPI的应用程序代码来说都是透明的。