dav*_*ewy 1 c++ mpi shared-memory openmpi
我有一个简单的C++结构,基本上包装一个标准的C数组:
struct MyArray {
T* data;
int length;
// ...
}
Run Code Online (Sandbox Code Playgroud)
其中T是数字类型,如float或double. length是数组中元素的数量.通常我的阵列非常大(数万到数千万个元素).
我有一个MPI程序,我希望通过MPI 3共享内存公开两个MyArray,例如a_old和a_new作为共享内存对象的实例.上下文是每个MPI等级读取a_old.然后,每个MPI等级写入某些索引a_new(每个等级只写入其自己的索引集 - 没有重叠).最后,a_old = a_new必须设置所有级别. a_old并且a_new大小相同.现在我通过将Isend/Irecv每个等级的更新值与其他等级同步()来使我的代码工作.但是,由于数据访问模式,我没有理由需要承担消息传递的开销,而是可以使用一个共享内存对象,然后再设置一个屏障a_old = a_new.我认为这会给我更好的表现(但如果我错了请纠正我).
我在查找使用MPI 3共享内存的完整代码示例时遇到了麻烦.大多数站点仅提供参考文档或不完整的代码段.有人可以通过一个简单而完整的代码示例来完成我正在尝试实现的事情(通过MPI共享内存更新和同步数字数组)吗?我理解创建共享内存通信器和窗口,设置围栏等的主要概念,但它确实有助于我的理解,看到一个将它们放在一起的例子.
另外,我应该提一下,我只会在一个节点上运行我的代码,因此我不需要担心跨节点需要多个共享内存对象副本; 我只需要为我的MPI进程运行的单个节点提供一份数据副本.尽管如此,在这种情况下,像OpenMP这样的其他解决方案对我来说也不可行,因为我有大量的MPI代码,并且为了我想分享的一个或两个数组而无法重写所有内容.
使用MPI-3共享内存相对简单.
首先,使用MPI_Win_allocate_shared以下命令分配共享内存窗口:
MPI_Win win;
MPI_Aint size;
void *baseptr;
if (rank == 0)
{
size = 2 * ARRAY_LEN * sizeof(T);
MPI_Win_allocate_shared(size, sizeof(T), MPI_INFO_NULL,
MPI_COMM_WORLD, &baseptr, &win);
}
else
{
int disp_unit;
MPI_Win_allocate_shared(0, sizeof(T), MPI_INFO_NULL,
MPI_COMM_WORLD, &baseptr, &win);
MPI_Win_shared_query(win, 0, &size, &disp_unit, &baseptr);
}
a_old.data = baseptr;
a_old.length = ARRAY_LEN;
a_new.data = a_old.data + ARRAY_LEN;
a_new.length = ARRAY_LEN;
Run Code Online (Sandbox Code Playgroud)
这里,只有0级分配内存.在共享时,哪个进程分配它并不重要.甚至可以让每个进程分配一部分内存,但由于默认情况下分配是连续的,因此两种方法都是等效的.MPI_Win_shared_query然后,所有其他进程使用它来查找共享内存块开头的虚拟地址空间中的位置.该地址可能因行列而异,因此不应传递绝对指针.
您现在可以a_old.data分别从中加载和存储a_new.data.由于您的案例中的排名适用于不相交的内存位置集,因此您不需要锁定窗口.使用窗口锁来实现例如受保护的初始化a_old或需要同步的其他操作.您可能还需要明确告诉编译器不要重新排序代码并发出内存栅栏,以便在调用之前完成所有未完成的加载/存储操作MPI_Barrier().
该a_old = a_new代码建议将一个数组复制到另一个数组上.相反,您可以简单地交换数据指针,最后交换大小字段.由于只有数组的数据在共享内存块中,因此交换指针是本地操作,即不需要同步.假设两个数组的长度相等:
T *temp;
temp = a_old.data;
a_old.data = a_new.data;
a_new.data = temp;
Run Code Online (Sandbox Code Playgroud)
在继续进行之前,您仍需要一个屏障来确保所有其他进程已完成处理.
在最后,只需释放窗口:
MPI_Win_free(&win);
Run Code Online (Sandbox Code Playgroud)
完整示例(在C中)如下:
#include <stdio.h>
#include <mpi.h>
#define ARRAY_LEN 1000
int main (void)
{
MPI_Init(NULL, NULL);
int rank, nproc;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Win win;
MPI_Aint size;
void *baseptr;
if (rank == 0)
{
size = ARRAY_LEN * sizeof(float);
MPI_Win_allocate_shared(size, sizeof(int), MPI_INFO_NULL,
MPI_COMM_WORLD, &baseptr, &win);
}
else
{
int disp_unit;
MPI_Win_allocate_shared(0, sizeof(int), MPI_INFO_NULL,
MPI_COMM_WORLD, &baseptr, &win);
MPI_Win_shared_query(win, 0, &size, &disp_unit, &baseptr);
}
printf("Rank %d, baseptr = %p\n", rank, baseptr);
int *arr = baseptr;
for (int i = rank; i < ARRAY_LEN; i += nproc)
arr[i] = rank;
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0)
{
for (int i = 0; i < 10; i++)
printf("%4d", arr[i]);
printf("\n");
}
MPI_Win_free(&win);
MPI_Finalize();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
免责声明:带上一粒盐.我对MPI的RMA的了解仍然很薄弱.