MPI_SEND 占用大量虚拟内存

Posted

技术标签:

【中文标题】MPI_SEND 占用大量虚拟内存【英文标题】:MPI_SEND takes huge part of virtual memory 【发布时间】:2012-10-16 19:52:25 【问题描述】:

在大量内核上调试我的程序时,我遇到了insufficient virtual memory 的非常奇怪的错误。我的调查导致代码和平,其中主人向每个奴隶发送小消息。然后我写了一个小程序,其中 1 个 master 简单地用MPI_SEND 发送 10 个整数,所有 slave 用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        

【问题讨论】:

您使用的是哪种 MPI 实现,这是在哪种网络上? 今天是星期五,大多数 SO 常客早已耗尽了他们的法力,并且无法猜测您在问题中未提供的内容。从注册内存的数量来看,我猜你是在 InfiniBand 或另一个支持 RDMA 的网络上运行的。从数据段的大小和注册内存的数量来看,我还猜想您并没有为所有发送操作重用相同的缓冲区,而是不断地分配一个新的缓冲区。请告诉我们您使用的 MPI 库并向我们展示您的发件人的源代码,以证明我错了。 MPI:impi-4.0.3 编译器:intel-13.0 QDR Infiniband 4x/10G 以太网/千兆以太网 【参考方案1】:

这是几乎所有在 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 来尝试使用动态预分配缓冲区大小。启用此选项后,英特尔 MPI 将首先注册小缓冲区,然后在需要时增加它们。另请注意,IMPI 会延迟打开连接,这就是为什么您仅在调用 MPI_Send 后才会看到已用内存大幅增加的原因。

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

【讨论】:

感谢两位的完整回答!我真的很惊讶,因为我知道 10-20 Mb(1 个主设备,400 个从设备,消息大小 = 10 个整数)的内存大小增加,但是 300 Mb!而且我的问题必须使用数千个内核... 因此,我的大模型从 1000 个内核开始,并且在初始化阶段 master 分配了一些小参数。现在对我来说仍然很清楚的事情-(1)为什么内存增加如此之大?消息太小了! (2) 这种分配是否意味着现在主进程有非常大的缓冲区用于内部 MPI 目的,直到程序结束? 巨大的缓冲区主要是为了在基准测试中获胜 :) 人们看到延迟数字而忘记了内存使用情况。基本上,供应商运行基准测试并发现复制少于 X 字节的消息比进行内存注册更快。这样就变成了急切的缓冲区大小。我想这个问题现在已经得到承认:Hristo 指出了动态调整大小功能。内部缓冲区将保持注册状态,直到程序结束。更糟糕的是,它们被固定在物理内存中,甚至不能被换出(因此你的虚拟内存错误)。 (3) “重用缓冲区”是什么意思?我希望 MPI 不会为我在 MPI_SEND 中使用的每个新消息数组分配新内存,对吗? @user1671232 (3) 不,我们的想法是在代码中重用缓冲区。如果您的消息大于急切阈值,则每次访问新缓冲区时都会先注册它(这很慢)。多次使用相同的缓冲区要快得多。但它对小消息没有帮助。 因此,例如,我有两个 MPI_SEND 调用,首先我将 Array1(1) 传递给第二个 - Array2(1000)。 IMPI 是否为这些数组分配两个不同的内存缓冲区?【参考方案2】:

Hristo 的回答大多是正确的,但由于您使用的是小消息,因此存在一些差异。消息最终在急切路径上:它们首先被复制到一个已经注册的缓冲区,然后该缓冲区用于传输,接收者将消息从其末端的急切缓冲区中复制出来。在代码中重用缓冲区只会帮助处理大消息。

这样做正是为了避免注册用户提供的缓冲区缓慢。对于大型消息,复制所需的时间比注册时间长,因此使用会合协议。

这些急切的缓冲区有些浪费。例如,在英特尔 MPI 上,它们默认为 16kB,带有 OF 动词。除非使用消息聚合,否则每条 10 整数大小的消息都会占用四个 4kB 页面。但无论如何,在与多个接收者交谈时,聚合都无济于事。

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


编辑:所以让它运行的最终解决方案是设置I_MPI_DAPL_UD=enable。我可以推测魔法的起源,但我无法访问英特尔的代码来实际确认这一点。

IB可以有不同的传输模式,其中两种是RC(Reliable Connected)和UD(Unreliable Datagram)。 RC 需要主机之间的显式连接(如 TCP),并且每个连接都会消耗一些内存。更重要的是,每个连接都有与之相关的急切缓冲区,这确实加起来了。这就是您使用英特尔的默认设置所获得的。

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

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

【讨论】:

以上是关于MPI_SEND 占用大量虚拟内存的主要内容,如果未能解决你的问题,请参考以下文章

VMware虚拟机磁盘操作占用过高问题

进程过多,占用大量内存,如何分析

如何查看虚拟内存使用情况

VMware虚拟机突然CPU高占用,内存异常?

Windows总提示虚拟内存太小怎么回事?怎样解决这个问题 ?

C#编写的程序 使用Webbrowser控件,占用大量内存?