通过 MPI 发送函数指针

Send function pointer via MPI

通过 MPI 传递函数指针作为告诉另一个节点调用函数的方式是否安全?有人可能会说通过MPI传递任何类型的指针都是没有意义的,但我写了一些代码来验证它。

//test.cpp
#include <cstdio>
#include <iostream>
#include <mpi.h>
#include <cstring>

using namespace std;

int f1(int a){return a + 1;}
int f2(int a){return a + 2;}
int f3(int a){return a + 3;}

using F=int (*)(int);

int main(int argc, char *argv[]){
    MPI_Init(&argc, &argv);
    int rank, size;
    MPI_Status state;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    //test
    char data[10];
    if( 0 == rank ){
        *(reinterpret_cast<F*>(data))=&f2;
        for(int i = 1 ; i < size ; ++i)
            MPI_Send(data, 8, MPI_CHAR, i, 0, MPI_COMM_WORLD);
    }else{
        MPI_Recv(data, 8, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &state);
        F* fp = reinterpret_cast<F*>(data);
        int ans = (**fp)(10);
        cout << ans << endl;
    }


    MPI_Finalize();
    return 0;
}

这是输出:

12
12
12
12
12
12
12
12
12

我通过 MVAPICH 运行 它,它运行良好。 但我现在不明白为什么,因为单独的地址空间意味着指针值在生成它的进程之外的任何进程中都是无用的

P.S。这是我的主机文件

blade11:1
blade12:1
blade13:1
blade14:1
blade15:1
blade16:1
blade17:1
blade18:2
blade19:1

和我运行mpiexec -n 10 -f hostfile ./test,并使用C++11编译它

从某种意义上说,您很幸运,因为您的集群环境是同构的,并且没有针对普通可执行文件的地址 space 随机化。因此,所有图像都加载到相同的基地址并在内存中以相似的方式布局,因此函数在所有 MPI 等级中具有相同的虚拟地址(请注意,对于来自动态链接库的符号,这很少是真的,因为它们通常加载在随机地址)。

如果您使用不同的编译器或使用相同的编译器但使用不同的编译器选项编译源代码两次,那么有一些排名 运行 第一个可执行文件,其余的 运行 第二个,程序肯定会崩溃。

试试这个:

$ mpicxx -std=c++11 -O0 -o test_O0 test.cpp
$ mpicxx -std=c++11 -O2 -o test_O2 test.cpp
$ mpiexec -f hostfile -n 5 ./test_O0 : -n 5 ./test_O2
12
12
12
12
<crash>

不同级别的优化导致 test_O0test_O2 中的函数代码大小不同。因此,f2 将不再在所有列中具有相同的虚拟地址。 运行 与等级 0 相同的可执行文件的等级将打印 12,而其余的将出现段错误。

Is it safe to pass function pointers via MPI as a way of telling another node to call a function?

不,不是。地址 space 未在进程之间共享。

但是,作为从同一源构建的程序的结果的 MPI 进程可以组织为在收到特定消息时调用特定函数:

char data = 0;
MPI_Recv(data, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &state);

if (data == 255) { 
    f2(10); /* and so forth */
}

没有。

然而,有一个涉及宏的技巧,将函数的特定编码映射到可以在所有进程中统一识别的局部函数 pointer/callback。 例如,这在 HPX http://stellar.cct.lsu.edu/files/hpx_0.9.5/html/HPX_PLAIN_ACTION.html 到 运行 跨非均匀系统的函数中使用。