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;

其中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 和 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 Fortran 矩阵(按行)

如何使用 MPI_Scatter 和 MPI_Gather 计算多个进程的平均值?

使用 MPI_Send 和 MPI_Recv 实现 MPI_Scatter 的问题

如何在 MPI_Scatter 的数组中分散多个变量

将 MPI_Scatter 与 MPI_Type_vector 一起使用时列的意外分布

如何使用 MPI 传输带有动态数组的自定义结构?