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:ofa
和 I_MPI_OFA_USE_XRC=1
来启用 XRC 支持(使 Intel MPI 使用 OFA 接口而不是 DAPL)。
当您切换到 UD 传输时,您会在缓冲区共享之上获得进一步优化:不再需要跟踪连接。在这个模型中,缓冲区共享是很自然的:因为没有连接,所有的内部缓冲区都在一个共享池中,就像 SRQ 一样。因此可以进一步节省内存,但要付出一定的代价:数据报传递可能会失败,这取决于软件,而不是 IB 硬件来处理重传。当然,这对于使用 MPI 的应用程序代码来说都是透明的。
【讨论】:
以上是关于MPI_SEND 占用大量虚拟内存的主要内容,如果未能解决你的问题,请参考以下文章