是否有一种干净的方法可以使用MPI_Scatter来分散不涉及打包或解压缩的结构?
我们说我有这样的结构:
struct Foo
{
int a;
int* b;
int* c;
}
a
和b
是"数组"可以如下实例化的整数:
struct Foo f1;
f1.a = 0;
f1.b = malloc(sizeof(int) * 10));
f1.c = malloc(sizeof(int) * 15));
我有一组Foo
个实例,其中每个实例的大小都不同b
和c
。
我可以为每个实例定义新的MPI类型,并使用MPI_Send发送它们,但显然,它不是很聪明。
所以我的问题是,MPI是否有内置支持?
答案 0 :(得分:1)
不幸的是,没有简单方式通过MPI传输数据,尽管显然并非如此简单方式。
问题的核心在于,您要传输的数据,即包含数据和指向其他数据的指针的结构,不是自包含的:结构内部的指针仅引用您要传输的数据的一部分,他们不包含它。因此,仅使用MPI_Type_create_struct()
创建MPI结构化类型不允许您传输逻辑所包含的所有数据,只允许实际包含的数据。
但是,您仍然可以在一些MPI通信中执行此操作,为方便起见,您可以将其包装在一个函数中。但要使它可行,你必须确定一些事情:
malloc
遍布各处的数据。 malloc
越少越好。除此之外,您可以尝试(如果在代码的上下文中尽可能)尝试分配与您b
的所有c
和/或struct Foo
字段相对应的单个大数据集},并使指针b
指向它在这个大数组中的份额。从MPI通信的角度来看,这将更简单,对于您的代码,性能也会更好。a
。这是一个完整的例子,说明了它的工作原理:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct Foo {
int a;
int *b;
int *c;
} Foo;
int main( int argc, char *argv[] ) {
MPI_Init( &argc, &argv );
int rank, size;
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
MPI_Comm_size( MPI_COMM_WORLD, &size );
int len = 3;
Foo *array = malloc( len * sizeof( Foo ) );
// let's assume for simplicity that each process already knows the sizes of the individual arrays (it would need to be transmitted otherwise)
int lenB[] = { 1, 2, 3 };
int lenC[] = { 5, 6, 7 };
// now we create the data for the arrays
int lenAllBs = 0, lenAllCs = 0;
for ( int i = 0; i < len; i++ ) {
lenAllBs += lenB[i];
lenAllCs += lenC[i];
}
int *BandC = malloc( ( lenAllBs + lenAllCs ) * sizeof( int ) );
// And we adjust the pointers
array[0].b = BandC;
array[0].c = BandC + lenAllBs;
for ( int i = 1; i < len; i++ ) {
array[i].b = array[i-1].b + lenB[i];
array[i].c = array[i-1].c + lenC[i];
}
// Now we create the MPI structured type for Foo. Here a resized will suffice
MPI_Datatype mpiFoo;
MPI_Type_create_resized( MPI_INT, 0, sizeof( Foo ), &mpiFoo );
MPI_Type_commit( &mpiFoo );
// Ok, the preparation phase was long, but here comes the actual transfer
if ( rank == 0 ) {
// Only rank 0 has some meaningful data
for ( int i = 0; i < len; i++ ) {
array[i].a = i;
for ( int j = 0; j < lenB[i]; j++ ) {
array[i].b[j] = 10 * i + j;
}
for ( int j = 0; j < lenC[i]; j++ ) {
array[i].c[j] = 100 * i + j;
}
}
// Sending it to rank size-1
// First the structure shells
MPI_Send( array, len, mpiFoo, size - 1, 0, MPI_COMM_WORLD );
// Then the pointed data
MPI_Send( BandC, lenAllBs + lenAllCs, MPI_INT, size - 1, 0, MPI_COMM_WORLD );
}
if ( rank == size - 1 ) {
// Receiving from 0
// First the structure shells
MPI_Recv( array, len, mpiFoo, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
// Then the actual data
MPI_Recv( BandC, lenAllBs + lenAllCs, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
// And printing some
printf( "array[1].a = %d, array[2].b[1] = %d, array[0].c[4]=%d\n", array[1].a, array[2].b[1], array[0].c[4] );
}
MPI_Type_free( &mpiFoo );
free( BandC );
free( array );
MPI_Finalize();
return 0;
}
使用mpicc -std=c99 dyn_struct.c -o dyn_struct
编译,它给了我:
$ mpirun -n 2 ./dyn_struct
array[1].a = 1, array[2].b[1] = 21, array[0].c[4]=4
正如您所看到的,一旦正确创建结构,它是可行的,并且不会太复杂。如果在传输之前不知道每个成员数据的各个大小,则必须在传输实际数据之前传输,并且必须在接收之前相应地设置接收缓冲区和结构。