通过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运行它,它运行良好。 但我现在不知道为什么因为单独的地址空间意味着指针值在除生成它之外的任何进程中都是USELESS 。
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
答案 0 :(得分:7)
您很幸运,因为您的群集环境是同构的,并且没有适用于普通可执行文件的地址空间随机化。因此,所有图像都加载到相同的基址并在内存中类似地布局,因此函数在所有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_O0
和test_O2
中不同大小的功能代码。因此,f2
将不再在所有排名中具有相同的虚拟地址。运行与排名0相同的可执行文件的排名将打印12
,而其余排名将为段错误。
答案 1 :(得分:2)
通过MPI传递函数指针作为告诉另一个节点调用函数的方法是否安全?
不,不是。地址空间不在进程之间共享。
但是,可以组织MPI进程,这些进程是由相同源构建的程序的结果,以便在收到某个消息时调用特定函数:
char data = 0;
MPI_Recv(data, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &state);
if (data == 255) {
f2(10); /* and so forth */
}
答案 2 :(得分:0)
没有。
然而,有一些涉及宏的技巧将函数的某个编码映射到可以在所有进程中统一识别的本地函数指针/回调。 例如,这在HPX http://stellar.cct.lsu.edu/files/hpx_0.9.5/html/HPX_PLAIN_ACTION.html中用于在不同系统中运行函数。