c++ - 通过包装器传递指向 MPI_Win_allocate_shared 的指针
问题描述
我很难理解我的指针传递发生了什么:
我有以下最小程序
#include <mpi.h>
void ALLOC_SHM(double * arr, int sz);
void MPI_WRAP( MPI_Aint size, int disp, MPI_Comm comm, double * bufptr, MPI_Win* win ) ;
int main(int argc, char const *argv[])
{
int size, whoami;
double* arr;
MPI_Init(NULL, NULL) ;
MPI_Comm_size( MPI_COMM_WORLD, &size );
MPI_Comm_rank(MPI_COMM_WORLD, &whoami);
ALLOC_SHM(arr, 1000);
return 0;
}
void ALLOC_SHM(double * arr, int sz)
{
MPI_Win win;
MPI_Aint size = sz*sizeof(double);
int disp = sizeof(double);
printf("in alloc before mpi_wrap: %p\n", arr);
MPI_WRAP(size, disp, MPI_COMM_WORLD, arr, &win);
printf("in alloc AFTER mpi_wrap: %p\n", arr);
return;
}
void MPI_WRAP( MPI_Aint size, int disp, MPI_Comm comm, double * bufptr, MPI_Win* win )
{
printf("in mpi_wrap before WIN_ALLOC: %p\n", bufptr);
int ierr = MPI_Win_allocate_shared( size, disp, MPI_INFO_NULL, comm, &bufptr, win );
printf("in mpi_wrap AFTER WIN_ALLOC: %p\n", bufptr);
return;
}
我的疑惑点:
对 MPI_WRAP 的调用是错误的,但为什么呢?是不是因为 MPI Api 需要指针的地址(即
**arr
)如果上述原因是正确的,那么可以通过将地址传递给指针来解决,所以我的 CALL 是
MPI_WRAP(size, disp,MPI_COMM_WORLD, &arr, &win);
现在,如果这是正确的 (2),我意外地意识到我的代码在不更改
MPI_WRAP
界面的情况下运行它没有问题。但很明显,我不是传递一个指针 (*bufptr
),而是传递一个**buftr
. 然后我将我的MPI_WRAP
界面更改为以下内容MPI_WRAP( MPI_Aint size, int disp, MPI_Comm comm, double ** bufptr, MPI_Win* win )
(我将其更改为双指针,并仍然按照 (2) 中的方式调用它)。令人惊讶的是,这也有效。我与 MPI 合作了足够长的时间,知道仅仅因为它现在可以工作,并不意味着它是正确的——而且你很幸运它适用于你的情况——因此,这里发生了什么,我为什么可以同时考虑接口和他们似乎都工作?
解决方案
阅读 MPI 标准并理解 C++ 指针和函数参数可能会有所帮助。例如,阅读 Open MPI 手册页MPI_Win_allocate_shared
:
在每个进程上,它分配至少size个字节的内存,在comm中的所有进程之间共享,并返回一个指向baseptr中本地分配的段的指针,该指针可用于调用进程的加载/存储访问。
C++ 函数通过函数参数返回值的唯一方法是该参数是引用还是指向该值的位置的指针。因此,虽然手册页列出baseptr
了void *
,但它确实是类型void **
。
现在,两者之间的区别:
void foo(void *bar) {
MPI_Win_allocate_shared(..., &bar, ...);
}
void *baz;
foo(baz);
和
void foo(void **bar) {
MPI_Win_allocate_shared(..., bar, ...);
}
void *baz;
foo(&baz);
尽管在这两种情况下,对的调用都以MPI_Win_allocate_shared
avoid **
作为参数,但前一种情况在概念上是错误的。您传递的不是 的地址baz
,而是一个指向正式参数的指针,该参数bar
包含的值的副本baz
。形式参数的语义基本上是用实际函数参数初始化的局部变量的语义:
void *bar = baz;
MPI_Win_allocate_shared(..., &bar, ...);
这将写入一个新值bar
,同时保持 的值baz
不变。这就是为什么您在调用MPI_Win_allocate_shared
inside后会看到一个新值,MPI_WRAP
但在返回调用函数后会看到旧值。
后者类似于
void **bar = &baz;
MPI_Win_allocate_shared(..., bar, ...);
这具有完全不同的语义。bar
现在包含的地址,baz
这就是MPI_Win_allocate_shared
将分配的缓冲区的地址写入的位置。
所以正确的 C/C++ 代码是:
void foo(void **bar) {
MPI_Win_allocate_shared(..., bar, ...);
}
void *baz;
foo(&baz);
C++ 有引用,同样可以这样写:
void foo(void *&bar) {
MPI_Win_allocate_shared(..., &bar, ...);
}
void *baz;
foo(baz);
这与第一种(不正确的)情况非常相似,关键区别在于这里的形式参数bar
是实际参数 的别名baz
,所以 now&bar
与 相同&baz
。因此,MPI_Win_allocate_shared
会将返回值写入 的存储空间baz
。
附注:我推荐 Open MPI 的手册页,因为其中的解释基本上是 MPI 标准的摘录。
推荐阅读
- c++ - 使用 Poco::NotificationCenter 在通知处理程序中发送通知时出现死锁
- javascript - 构建插件/库时的构造函数问题
- javascript - 当没有错误时,为什么我的错误样式会应用于元素?
- vue.js - Vue无法读取未定义的属性'$refs'
- r - 回归循环中表的新对象
- python - 如何从 Tensorflow 中的冻结模型(pb 文件)中找到 output_node_names?
- c# - 解析 json 时出现许多空条目
- vb.net - win32 api 查找特定进程访问的文件
- php - 将 mysqli 转换为 pdo 准备好的语句
- tensorflow - 需要使用 if 语句的自定义损失函数