MPI_SEND占据了虚拟内存的很大一部分

时间:2012-10-26 14:24:00

标签: linux mpi infiniband intel-mpi

在大量内核上调试我的程序,我遇到了insufficient virtual memory的非常奇怪的错误。我的调查导致代码的和平,主机向每个从机发送小消息。然后我编写了一个小程序,其中1个master只用MPI_SEND发送10个整数,所有从站都用MPI_RECV接收它。 /proc/self/status之前和之后的文件MPI_SEND的比较显示,内存大小之间的差异非常大!最有趣的事情(崩溃我的程序)是,这个内存不会在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        

2 个答案:

答案 0 :(得分:10)

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

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

有一些参数可以控制英特尔MPI使用的IB队列。在您的情况下,最重要的是I_MPI_DAPL_BUFFER_NUM,它控制预分配和预注册内存的数量。它的默认值为16,因此您可能希望减少它。但请注意可能的性能影响。您还可以通过将I_MPI_DAPL_BUFFER_ENLARGEMENT设置为1来尝试使用动态预分配缓冲区大小。启用此选项后,Intel MPI最初会注册小缓冲区,如果需要,稍后会增加它们。另请注意,IMPI会延迟打开连接,这就是为什么只有在调用MPI_Send后才能看到已用内存的大幅增加。

如果不使用DAPL传输,例如相反,使用ofa传输,您无能为力。您可以将I_MPI_OFA_USE_XRC设置为1来启用XRC队列。这应该以某种方式减少使用的内存。如果程序的通信图形未完全连接,则通过将I_MPI_OFA_DYNAMIC_QPS设置为1来启用动态队列对创建可能会减少内存使用量(完全连接的程序是每个等级与所有其他等级对话的程序) )。

答案 1 :(得分:5)

Hristo的答案大多是正确的,但由于你使用的是小消息,所以有一点不同。消息最终出现在急切路径上:它们首先被复制到已经注册的缓冲区,然后该缓冲区用于传输,接收方将消息从其末端的急切缓冲区中复制出来。在代码中重用缓冲区只会对大型消息有所帮助。

这样做是为了避免注册用户提供的缓冲区的速度慢。对于大型消息,副本比注册时间长,因此使用集合协议。

这些热切的缓冲区有些浪费。例如,默认情况下,它们在具有OF动词的英特尔MPI上为16kB。除非使用消息聚合,否则每个10-int大小的消息正在占用4个4kB页面。但无论如何,在与多个接收器通信时,聚合将无济于事。

那该怎么办?减少热切缓冲区的大小。这可以通过设置eager / rendezvous阈值(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扩展,它进一步实现了队列共享:在同一节点上的进程之间。 默认情况下,Intel的MPI通过DAPL访问IB设备,而不是直接通过OF动词访问IB设备。我的猜测是这排除了这些优化(我没有DAPL的经验)。通过设置I_MPI_FABRICS=shm:ofaI_MPI_OFA_USE_XRC=1(使英特尔MPI使用OFA接口而不是DAPL),可以启用XRC支持。

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