C 中的 MPI_Scatter 结构
Posted
技术标签:
【中文标题】C 中的 MPI_Scatter 结构【英文标题】:MPI_Scatter structs in C 【发布时间】:2016-01-19 21:43:15 【问题描述】:有没有一种干净的方法来使用 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 是否对此有任何内置支持?
【问题讨论】:
是的,看来 MPI 和 SO 都支持这一点。见***.com/questions/9864510/… 【参考方案1】:很遗憾,没有简单方法可以通过 MPI 传输您的数据,尽管显然不是那么简单方法。
这里的问题的核心是您要传输的数据,即包含数据和指向其他数据的指针的结构不是自包含的:结构内的指针仅引用您要传输的部分数据, 他们不包含它。因此,简单地使用 MPI_Type_create_struct()
创建 MPI 结构化类型将不允许您传输结构逻辑上包含的所有数据,只能传输它实际包含的数据。 p>
但是,您仍然可以在一些 MPI 通信中实现这一目的,您可以将其包装在一个函数中以方便使用。但要使其可行,您必须确保以下几点:
-
尽量避免
malloc
在各处发送您的数据。 malloc
越少越好。取而代之的是,您可以尝试(如果可能的话,在您的代码的上下文中)分配一个与您的所有b
和/或c
字段对应的单个大型数据数组struct Foo
,并制作指针@ 987654327@ 指向它在这个大数组中的份额。从 MPI 通信的角度来看,这既更简单,也为您的代码提供更好的性能。
创建一个空心 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
如您所见,一旦正确创建了结构,它是可行的,而且不会太复杂。如果在传输之前不知道每个成员数据的各个大小,则必须在传输实际数据之前传输它,并且必须在接收之前相应地设置接收缓冲区和结构。
【讨论】:
感谢您提供非常详细的答案,但我认为我的问题不够清楚。我知道如何定义 mpi 类型以与 MPI_Send 一起使用以发送我的结构,但我正在寻找一种“一次性”执行 MPI_scatter 的方法。但似乎我必须先分散尺寸信息,然后分散实际数据。【参考方案2】:只需使用 MPI_CHAR“按字节”发送结构数组
MPI_Scatter(
array,
number_of_elements * sizeof(YOUR_STRUCT),
MPI_CHAR,
proc_array,
number_of_elements * sizeof(YOUR_STRUCT),
MPI_CHAR,
0,
MPI_COMM_WORLD
);
【讨论】:
对于简单的结构当然可以,但这不适用于指针,地址在其他进程中只是垃圾。以上是关于C 中的 MPI_Scatter 结构的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 MPI_Scatter 和 MPI_Gather 计算多个进程的平均值?
使用 MPI_Send 和 MPI_Recv 实现 MPI_Scatter 的问题