0

我有一个使用 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 limit\n");
            failed = 1;
        }
    } while (!failed);
}

使用 just 编译和运行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

这里gcc没有mpicc任何变化。当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 时,将标准替换为mallocPAL 中自己的标准,这在异常情况下无法正常工作。有没有办法绕过它(例如,通过某种方式将 mybigalloc.cglibc静态链接)?

4

1 回答 1

0

Open MPI 通过提供的挂钩机制进行拦截malloc()free()调用,glibc以便它可以跟踪动态分配和解除分配的缓冲区。这样做是因为像 InfiniBand 这样的基于 RDMA 的网络需要将通信缓冲区固定(即,使其不可移动)在物理内存中,以便硬件可以访问它们。注册和取消注册内存(固定和取消固定它的过程)需要相当长的时间,这就是为什么库根本不取消固定已经固定的内存以希望它可以被重用(这就是为什么动态分配每个通信缓冲区不仅仅是非常馊主意)。但是如果内存是动态分配的,然后注册然后释放,这可能会导致问题。这就是 Open MPI 挂钩的原因malloc/freeAPI 并跟踪动态分配。如果硬件支持,Open MPI 还可以使用 MMU 通知跟踪内存,并且相应地构建了库。

至少在较新的 Open MPI 版本中,可以通过将memory_linux_disableMCA 参数设置为 来禁用内存挂钩1。与所有其他 MCA 变量不同,该变量只能通过环境设置,例如,必须将环境变量设置OMPI_MCA_memory_linux_disable1并导出。如果程序将在具有 InfiniBand 或其他基于 RDMA 的网络的集群上运行,请不要这样做!不幸的是,您正在运行一个无法识别此 MCA 参数的古老版本的 Open MPI,并且该memory_hooks模块似乎没有提供可以禁用它的机制。你真的应该在 Open MPI 用户邮件列表上询问。

另请注意,Linux 内核在默认情况下会过度使用内存,即它们允许分配超过物理内存大小的虚拟内存。最终结果是,malloc()如果您(或任何 Open MPI 挂钩程序)尝试使用该内存,那么在内存大小比可用物理内存大小大得多的情况下会成功,在某些时候物理内存将被耗尽并且进程将收到SIGSEGV.

于 2013-04-10T14:39:05.850 回答