openmpi:如何将未连接的数据块从一个等级发送到所有其他等级?
openmpi: how to send a not connected datablock from one rank to all other ranks?
我正在寻找一个 MPI function/method,它允许将多个数据块从一个进程传送到所有其他进程。类似于 MPI_Bcast 但同时有多个方块?
我在根等级上有一个碎片化的数据块:
#define BLOCKS 5
#define BLOCKSIZE 10000
char *datablock[BLOCKS];
int i;
for (i=0; i<BLOCKS; i++) datablock[i] = (char*)malloc(BLOCKSIZE*sizeof(char))
这只是一个例子,但很明显,BLOCKS 不一定是相邻的。我想把这个数据块传送到所有其他级别(我已经准备好必要的内存来存储它)。
我注意到有像 MPI_Gatherv 或 MPI_Scatterv 这样的方法允许使用位移数组收集或分散碎片数据,问题是分散将每个碎片发送到不同的等级,我需要将所有片段发送到所有其他行列,例如带有位移信息的 MPI_Bcast,例如 MPI_Bcastv.
一个解决方案是进行多次 MPI_Bcast 调用(每个块调用一次),但我不确定这是否是最好的方法。
更新:
我会尝试这里的 MPI_Ibcast 方法,我认为应该可行:
int rank; // rank id
int blocksize = 10000;
int blocknum = 200;
char **datablock = NULL;
char *recvblock = NULL;
MPI_Request *request;
request = (MPI_Request *)malloc(blocknum*sizeof(MPI_Request));
if(rank == 0) {
// this is just an example in practice those blocks are created one the fly as soon as the last block is filled
datablock = (char**)malloc(blocknum*sizeof(char*));
for (i=0; i<BLOCKS; i++) datablock[i] = (char*)malloc(blocksize*sizeof(char));
for (i=0; i<blocknum; i++)
MPI_Ibcast(datablock[i], blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, request[i]);
} else {
// for this example the other threads know allreay how many blocks the rank 0 has created, in practice this information is broadcasted via MPI before the MPI_Ibcast call
recvblock = (*char)malloc(blocksize*blocknum*sizeof(char));
for (i=0; i<blocknum; i++)
MPI_Ibcast(recvblock+i*(blocksize), blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, request[i]);
}
MPI_Waitall(blocknum, request, MPI_STATUSES_IGNORE);
所以,缺少一个MPI_Waitall,我不知道如何使用它,需要一个计数,一个请求数组和一个状态数组!?
我对根和其他等级有不同 MPI_Ibcast 的原因是,发送缓冲区与接收缓冲区不同。
另一个问题是,我是否需要对 for 循环中的每个 MPI_Ibcast 进行不同的请求,或者我是否可以像上面示例中那样重复使用 MPI_request 变量?
UPDATE2:所以我更新了示例,我现在使用 MPI_Request 指针!
然后我在定义之后立即通过 malloc 调用进行初始化,我猜这看起来很奇怪,但这只是一个例子,实际上所需的请求数只在运行时才知道。我特别担心我是否可以在这里使用 sizeof(MPI_Request) 或者这是否有问题因为这不是标准数据类型?
除此之外,这个例子是否正确?如果我想使用 MPI_Ibcast,这是一个好的解决方案吗?
序列化是个好主意吗?例如,您可以将多个缓冲区复制到一个缓冲区中,广播它,然后在接收方解压缩它。 It is the way boost.mpi
handles complex objects (in c++)
或者,您可以多次调用 MPI_Bcast()
的非阻塞版本:MPI_Ibcast()
,然后单次调用 MPI_Waitall()
。
请注意,您描述的数据看起来像一个二维数组。有一种方法可以以不同的方式分配它,以便整个数据在内存中是连续的:
int block=42;
int blocksize=42;
char **array=malloc(block*sizeof(char*));
if(array==NULL){fprintf(stderr,"malloc failed\n";exit(1);}
array[0]=malloc(block*blocksize*sizeof(char));
if(array[0]==NULL){fprintf(stderr,"malloc failed\n";exit(1);}
int i;
for(i=1;i<block;i++){
array[i]=&array[0][i*blocksize];
}
然后,对 MPI_Bcast()
的一次调用就足以广播整个数组:
MPI_Bcast(array[0], block*blocksize, MPI_CHAR,0, MPI_COMM_WORLD);
编辑:这是一个基于您的代码的解决方案,由 mpicc main.c -o main -Wall
和 运行 由 mpirun -np 4 main
编译:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
int size, rank;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
int i;
int blocksize = 10000;
int blocknum = 200;
char **datablock = NULL;
char *recvblock = NULL;
MPI_Request requests[blocknum];
MPI_Status status[blocknum];
if(rank == 0) {
// this is just an example in practice those blocks are created one the fly as soon as the last block is filled
datablock = malloc(blocknum*sizeof(char*));
if(datablock==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
for (i=0; i<blocknum; i++){
datablock[i] = (char*)malloc(blocksize*sizeof(char));
if(datablock[i]==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
datablock[i][0]=i%64;
}
for (i=0; i<blocknum; i++)
MPI_Ibcast(datablock[i], blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, &requests[i]);
} else {
// for this example the other threads know allreay how many blocks the rank 0 has created, in practice this information is broadcasted via MPI before the MPI_Ibcast call
recvblock = malloc(blocksize*blocknum*sizeof(char));
if(recvblock==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
for (i=0; i<blocknum; i++)
MPI_Ibcast(recvblock+i*(blocksize), blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, &requests[i]);
}
int ierr=MPI_Waitall(blocknum, requests, status);
if(ierr!=MPI_SUCCESS){fprintf(stderr,"MPI_Waitall() failed rank %d\n",rank);exit(1);}
if(rank==0){
for(i=0;i<blocknum;i++){
free(datablock[i]);
}
free(datablock);
}else{
for(i=0;i<blocknum;i++){
if(recvblock[i*(blocksize)]!=i%64){
printf("communcation problem ! %d %d %d\n",rank,i, recvblock[i*(blocksize)]);
}
}
free(recvblock);
}
MPI_Finalize();
return 0;
}
我认为最佳实现方式是将序列化和 MPI_IBcast()
结合起来,以限制内存占用和消息数量。
您可以使用多个 bcast(然后使用 Ibcast
可能是个好主意)但是如果您想一次发送所有内容,请检查 MPI_Type_hindexed
派生数据类型。
我正在寻找一个 MPI function/method,它允许将多个数据块从一个进程传送到所有其他进程。类似于 MPI_Bcast 但同时有多个方块?
我在根等级上有一个碎片化的数据块:
#define BLOCKS 5
#define BLOCKSIZE 10000
char *datablock[BLOCKS];
int i;
for (i=0; i<BLOCKS; i++) datablock[i] = (char*)malloc(BLOCKSIZE*sizeof(char))
这只是一个例子,但很明显,BLOCKS 不一定是相邻的。我想把这个数据块传送到所有其他级别(我已经准备好必要的内存来存储它)。
我注意到有像 MPI_Gatherv 或 MPI_Scatterv 这样的方法允许使用位移数组收集或分散碎片数据,问题是分散将每个碎片发送到不同的等级,我需要将所有片段发送到所有其他行列,例如带有位移信息的 MPI_Bcast,例如 MPI_Bcastv.
一个解决方案是进行多次 MPI_Bcast 调用(每个块调用一次),但我不确定这是否是最好的方法。
更新: 我会尝试这里的 MPI_Ibcast 方法,我认为应该可行:
int rank; // rank id
int blocksize = 10000;
int blocknum = 200;
char **datablock = NULL;
char *recvblock = NULL;
MPI_Request *request;
request = (MPI_Request *)malloc(blocknum*sizeof(MPI_Request));
if(rank == 0) {
// this is just an example in practice those blocks are created one the fly as soon as the last block is filled
datablock = (char**)malloc(blocknum*sizeof(char*));
for (i=0; i<BLOCKS; i++) datablock[i] = (char*)malloc(blocksize*sizeof(char));
for (i=0; i<blocknum; i++)
MPI_Ibcast(datablock[i], blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, request[i]);
} else {
// for this example the other threads know allreay how many blocks the rank 0 has created, in practice this information is broadcasted via MPI before the MPI_Ibcast call
recvblock = (*char)malloc(blocksize*blocknum*sizeof(char));
for (i=0; i<blocknum; i++)
MPI_Ibcast(recvblock+i*(blocksize), blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, request[i]);
}
MPI_Waitall(blocknum, request, MPI_STATUSES_IGNORE);
所以,缺少一个MPI_Waitall,我不知道如何使用它,需要一个计数,一个请求数组和一个状态数组!?
我对根和其他等级有不同 MPI_Ibcast 的原因是,发送缓冲区与接收缓冲区不同。
另一个问题是,我是否需要对 for 循环中的每个 MPI_Ibcast 进行不同的请求,或者我是否可以像上面示例中那样重复使用 MPI_request 变量?
UPDATE2:所以我更新了示例,我现在使用 MPI_Request 指针! 然后我在定义之后立即通过 malloc 调用进行初始化,我猜这看起来很奇怪,但这只是一个例子,实际上所需的请求数只在运行时才知道。我特别担心我是否可以在这里使用 sizeof(MPI_Request) 或者这是否有问题因为这不是标准数据类型?
除此之外,这个例子是否正确?如果我想使用 MPI_Ibcast,这是一个好的解决方案吗?
序列化是个好主意吗?例如,您可以将多个缓冲区复制到一个缓冲区中,广播它,然后在接收方解压缩它。 It is the way
boost.mpi
handles complex objects (in c++)或者,您可以多次调用
MPI_Bcast()
的非阻塞版本:MPI_Ibcast()
,然后单次调用MPI_Waitall()
。请注意,您描述的数据看起来像一个二维数组。有一种方法可以以不同的方式分配它,以便整个数据在内存中是连续的:
int block=42; int blocksize=42; char **array=malloc(block*sizeof(char*)); if(array==NULL){fprintf(stderr,"malloc failed\n";exit(1);} array[0]=malloc(block*blocksize*sizeof(char)); if(array[0]==NULL){fprintf(stderr,"malloc failed\n";exit(1);} int i; for(i=1;i<block;i++){ array[i]=&array[0][i*blocksize]; }
然后,对 MPI_Bcast()
的一次调用就足以广播整个数组:
MPI_Bcast(array[0], block*blocksize, MPI_CHAR,0, MPI_COMM_WORLD);
编辑:这是一个基于您的代码的解决方案,由 mpicc main.c -o main -Wall
和 运行 由 mpirun -np 4 main
编译:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
int size, rank;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
int i;
int blocksize = 10000;
int blocknum = 200;
char **datablock = NULL;
char *recvblock = NULL;
MPI_Request requests[blocknum];
MPI_Status status[blocknum];
if(rank == 0) {
// this is just an example in practice those blocks are created one the fly as soon as the last block is filled
datablock = malloc(blocknum*sizeof(char*));
if(datablock==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
for (i=0; i<blocknum; i++){
datablock[i] = (char*)malloc(blocksize*sizeof(char));
if(datablock[i]==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
datablock[i][0]=i%64;
}
for (i=0; i<blocknum; i++)
MPI_Ibcast(datablock[i], blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, &requests[i]);
} else {
// for this example the other threads know allreay how many blocks the rank 0 has created, in practice this information is broadcasted via MPI before the MPI_Ibcast call
recvblock = malloc(blocksize*blocknum*sizeof(char));
if(recvblock==NULL){fprintf(stderr,"malloc failed\n"); exit(1);}
for (i=0; i<blocknum; i++)
MPI_Ibcast(recvblock+i*(blocksize), blocksize, MPI_CHAR, 0, MPI_COMM_WORLD, &requests[i]);
}
int ierr=MPI_Waitall(blocknum, requests, status);
if(ierr!=MPI_SUCCESS){fprintf(stderr,"MPI_Waitall() failed rank %d\n",rank);exit(1);}
if(rank==0){
for(i=0;i<blocknum;i++){
free(datablock[i]);
}
free(datablock);
}else{
for(i=0;i<blocknum;i++){
if(recvblock[i*(blocksize)]!=i%64){
printf("communcation problem ! %d %d %d\n",rank,i, recvblock[i*(blocksize)]);
}
}
free(recvblock);
}
MPI_Finalize();
return 0;
}
我认为最佳实现方式是将序列化和 MPI_IBcast()
结合起来,以限制内存占用和消息数量。
您可以使用多个 bcast(然后使用 Ibcast
可能是个好主意)但是如果您想一次发送所有内容,请检查 MPI_Type_hindexed
派生数据类型。