gpt4 book ai didi

c - C 中的 MPI_Scatter 结构

转载 作者:行者123 更新时间:2023-11-30 15:16:04 26 4
gpt4 key购买 nike

是否有一种干净的方法来使用 MPI_Scatter 来分散不涉及打包或解包的结构?

假设我有一个这样的结构:

struct Foo
{
int a;
int* b;
int* c;
}

其中 ab 是整数“数组”,可以按如下方式实例化:

 struct Foo f1;
f1.a = 0;
f1.b = malloc(sizeof(int) * 10));
f1.c = malloc(sizeof(int) * 15));

我有一个 Foo 实例数组,其中每个实例都有不同大小的 bc

我可以为每个实例定义新的 MPI 类型并使用 MPI_Send 发送它们,但显然,这不是很聪明。

所以我的问题是,MPI 对此有内置支持吗?

最佳答案

不幸的是,没有简单的方法通过 MPI 传输数据,尽管显然有不那么简单的方法。

这里问题的核心是您要传输的数据,即包含数据和指向其他数据的指针的结构,不是自包含的:结构内的指针仅引用您要传输的数据的一部分,他们不包含它。因此,仅使用 MPI_Type_create_struct() 创建 MPI 结构化类型将不允许您传输结构逻辑上包含的所有数据,只能传输它实际 包含。

但是,您仍然可以在一些 MPI 通信中实现这一技巧,为了方便起见,您可以将其包装在函数中。但要使其可行,您必须确保以下几点:

  1. 尽量避免到处malloc您的数据。 malloc 越少越好。相反,您可以尝试(如果在代码上下文中可能的话)分配与所有 b 和/或 c 字段相对应的一个大型数据数组你的struct Foo,并让指针b指向它在这个大数组中的份额。从 MPI 通信的角度来看,这会更简单,而且代码的性能也会更好。
  2. 创建一个空心 MPI 结构化类型,该类型将仅包含该结构所包含的非动态数据,例如您的情况中的 a
  3. 然后分两个阶段传输数据:首先是结构,然后是包含结构的各个动态字段的各种大型数组。
  4. 最后,在接收端,调整(如果需要)指针以指向新接收的数据。

以下是其工作原理的完整示例:

#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

正如您所看到的,一旦结构正确创建,它是可行的,并且不会太复杂。如果在传输之前不知道每个成员数据的单独大小,则必须在传输实际数据之前传输它,并且必须在接收之前相应地设置接收缓冲区和结构。

关于c - C 中的 MPI_Scatter 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33250076/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com