我有一个使用MPI的古老而混乱的Fortran程序。它有一个用C编写的小模块,它试图通过迭代调用malloc()
来确定内存中最大的可分配块,直到它返回null
,然后将最大的成功分配大小返回给Fortran程序。
当我使用 gfortran
编译它时,它运行良好,但是当我尝试使用 mpif90
时,最后一个malloc()
会导致段错误而不是返回null
。
下面是没有实际 MPI 代码的最小说明性示例。文件main.f
:
program test
complex(8) :: sig(256000000) ! Just allocating some big array in fortran
sig(1) = 0.d0 ! and now wondering how much space is left?
call bigalloc
end
文件bigalloc.c
#include <stdlib.h>
#include <stdio.h>
void bigalloc_() {
size_t step = 0x80000000;
size_t size = 0;
int failed = 0;
void* p;
do {
size += step;
p = malloc(size);
if (p) {
free(p);
printf("Allocated %zd...n", size);
} else {
printf("So, that's our limitn");
failed = 1;
}
} while (!failed);
}
仅使用gfortran
编译和运行(按预期工作(:
~$ gcc -c bigalloc.c -o bigalloc.o && gfortran -o main main.f bigalloc.o && ./main
Allocated 2147483648...
Allocated 4294967296...
So, that's our limit
使用 MPI 编译并运行(失败(:
~$ gcc -c bigalloc.c -o bigalloc.o && mpif90 -o main main.f bigalloc.o && ./main
Allocated 2147483648...
Allocated 4294967296...
Segmentation fault
用mpicc
替换gcc
在这里没有任何变化。当main
也是用 C 编写并使用 mpicc
编译时,一切也正常。所以问题只出在Fortran上。
mpif90 -show
的输出在这里。问题完全取决于-lopen-pal
选项的存在。
$ mpif90 -show
gfortran -I/usr/include/openmpi/1.2.4-gcc/64 -I/usr/include/openmpi/1.2.4-gcc -m64 -pthread -I/usr/lib64/openmpi/1.2.4-gcc -L/usr/lib64/openmpi/1.2.4-gcc -lmpi_f90 -lmpi_f77 -lmpi -lopen-rte -lopen-pal -ldl -Wl,--export-dynamic -lnsl -lutil -lm -ldl
似乎在链接 MPI 时,将标准malloc
替换为 PAL 中自己的标准,这在异常情况下无法正常工作。有没有办法绕过它(例如,以某种方式将我的bigalloc.c
与静态glibc
联系起来(?
Open MPI 通过glibc
提供的挂钩机制拦截malloc()
和free()
调用,以便它可以跟踪动态分配和解除分配的缓冲区。这样做是因为基于RDMA的网络(如InfiniBand(要求将通信缓冲区固定在物理内存中(即不可移动(,以便硬件可以访问它们。注册和取消注册内存(固定和取消固定它的过程(需要相当长的时间,这就是为什么库根本不取消固定已经固定的内存,希望它会被重用(这就是为什么动态分配每个通信缓冲区不仅仅是非常糟糕的主意(。但是,如果动态分配内存,然后注册然后解除分配,这可能会导致问题。这就是 Open MPI 挂钩malloc/free
API 并跟踪动态分配的原因。Open MPI 还可以使用 MMU 通知跟踪内存,前提是硬件支持内存并且库已相应地构建。
内存挂钩,至少在较新的 Open MPI 版本中,可以通过将 memory_linux_disable
MCA 参数设置为 1
来禁用。与所有其他MCA变量不同,这个变量只能通过环境设置,例如,必须设置环境变量OMPI_MCA_memory_linux_disable
才能1
并导出它。如果程序将在具有InfiniBand或其他基于RDMA的网络的集群上运行,请不要这样做!不幸的是,您正在运行无法识别此 MCA 参数的古老版本的 Open MPI,并且memory_hooks
模块似乎没有提供可以禁用它的机制。您确实应该在Open MPI用户邮件列表中询问。
另请注意,Linux 内核默认过度使用内存,即它们允许超过物理内存大小的虚拟内存分配。最终结果是,malloc()
使用比可用物理内存大小大得多的内存大小会成功,但是如果您(或任何 Open MPI 挂钩过程(尝试使用该内存,则在某些时候物理内存将耗尽,并且进程将收到SIGSEGV
。