5

我在谷歌和这个网站上搜索了我的问题,但我仍然不明白解决方案。

我有一段MPI程序,其中RECV有一些数据。程序在大数组上崩溃,错误是虚拟内存不足,所以我开始考虑/proc/self/statusfile.

之前MPI_RECV是:

Name:   model.exe                                                               
VmPeak:   841640 kB
VmSize:   841640 kB
VmHWM:     15100 kB
VmRSS:     15100 kB
VmData:   760692 kB

之后:

Name:   model.exe                                                            
VmPeak:   841640 kB
VmSize:   841640 kB
VmHWM:    719980 kB
VmRSS:    719980 kB
VmData:   760692 kB

我在 Ubuntu 上测试它,通过系统监视器我看到这​​个内存在增加。VmSize但我很困惑(和VmPeak)参数没有变化。

问题是——实际内存使用的指标是什么?

这是否意味着,真正的指标是VmRSS?(并且VmSize仅分配但仍未使用的内存)

4

1 回答 1

28

(您的问题的可能解决方案是最后一段)

大多数具有虚拟内存的现代操作系统上的内存分配是一个两阶段的过程。首先,进程的一部分虚拟地址空间被保留,进程的虚拟内存大小(VmSize)相应增加。这会在所谓的进程页表中创建条目。页面最初不与物理内存帧相关联,即实际上没有使用物理内存。每当实际读取或写入此分配部分的某些部分时,就会发生页面错误,并且操作系统会从物理内存中安装(映射)一个空闲页面。这会增加进程的驻留集大小(VmRSS)。当一些其他进程需要内存时,操作系统可能会将一些不常用页面的内容(“不常用页面”的定义高度依赖于实现)存储到一些持久存储(大多数情况下是硬盘驱动器,或者通常存储到交换设备) ) 然后取消映射。此过程会减少 RSS,但会VmSize保持原样。如果稍后访问该页面,则会再次发生页面错误并将其带回。仅当释放虚拟内存分配时,虚拟内存大小才会减小。请注意,VmSize内存映射文件(即可执行文件和它链接到的所有共享库或其他显式映射文件)和共享内存块也计算在内。

进程中有两种通用类型的内存——静态分配的内存和堆内存。静态分配的内存保留所有常量和全局/静态变量。它是数据段的一部分,其大小由VmData公制。数据段还承载部分程序堆,其中分配了动态内存。数据段是连续的,即它从某个位置开始向上向堆栈增长(从一个非常高的地址开始然后向下增长)。数据段中的堆的问题在于它由一个特殊的堆分配器管理,该分配器负责将连续的数据段细分为更小的内存块。另一方面,在Linux中动态内存也可以通过直接映射虚拟内存来分配。这通常只用于大分配以节省内存,因为它只允许分配页面大小(通常为 4 KiB)倍数的内存。

堆栈也是大量内存使用的重要来源,尤其是在自动(堆栈)存储中分配大数组时。堆栈从可用虚拟地址空间的最顶部附近开始并向下增长。在某些情况下,它可能会到达数据段的顶部,或者可能会到达其他一些虚拟分配的末尾。坏事就会发生。堆栈大小计入VmStack公制,也计入VmSize. 可以这样概括:

  • VmSize占所有虚拟内存分配(文件映射、共享内存、堆内存、任何内存),并且几乎每次分配新内存时都会增长。几乎,因为如果新的堆内存分配是在数据段中释放的旧分配的位置进行的,则不会分配新的虚拟内存。每当释放虚拟分配时,它就会减少。VmPeak跟踪的最大值VmSize- 它只能随时间增加。
  • VmRSS随着内存被访问而增长,随着内存被分页到交换设备而减少。
  • VmData随着堆的数据段部分被使用而增长。它几乎永远不会缩小,因为当前的堆分配器会保留释放的内存以防将来的分配需要它。

如果您在使用 InfiniBand 或其他基于 RDMA 的结构的集群上运行,则另一种内存会发挥作用 - 锁定(注册)内存 ( VmLck)。这是不允许被调出的内存。它如何增长和缩小取决于 MPI 实现。有些人从不注销已经注册的块(关于为什么这里描述的技术细节太复杂了),其他人这样做是为了更好地使用虚拟内存管理器。

在您的情况下,您说您遇到了虚拟内存大小限制。这可能意味着此限制设置得太低,或者您遇到了操作系统强加的限制。首先,Linux(和大多数 Unix)有办法通过该ulimit机制施加人为的限制。在 shell 中运行ulimit -v会告诉您虚拟内存大小的限制(以 KiB 为单位)。您可以使用 设置限制ulimit -v <value in KiB>。这仅适用于由当前 shell 生成的进程及其子、孙等。您需要指导mpiexec(或mpirun) 将此值传播到所有其他进程,如果它们要在远程节点上启动。如果您在一些工作负载管理器(如 LSF、Sun/Oracle Grid Engine、Torque/PBS 等)的控制下运行程序,则有一些作业参数可以控制虚拟内存大小限制。最后但同样重要的是,32 位进程通常限制为 2 GiB 的可用虚拟内存。

于 2012-11-09T13:54:21.273 回答