【问题标题】:MPI_Scatter structs in CC 中的 MPI_Scatter 结构
【发布时间】: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 是否对此有任何内置支持?

【问题讨论】:

标签: c struct mpi


【解决方案1】:

很遗憾,没有简单方法可以通过 MPI 传输您的数据,尽管显然不是那么简单方法。

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

但是,您仍然可以在一些 MPI 通信中实现这一目的,您可以将其包装在一个函数中以方便使用。但要使其可行,您必须确保以下几点:

  1. 尽量避免malloc在各处发送您的数据。 malloc 越少越好。取而代之的是,您可以尝试(如果可能的话,在您的代码的上下文中)分配一个与您的所有b 和/或c 字段对应的单个大型数据数组struct Foo,并制作指针@ 987654327@ 指向它在这个大数组中的份额。从 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

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

【讨论】:

  • 感谢您提供非常详细的答案,但我认为我的问题不够清楚。我知道如何定义 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
);

【讨论】:

  • 对于简单的结构当然可以,但这不适用于指针,地址在其他进程中只是垃圾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
  • 2011-07-18
  • 2014-12-18
  • 1970-01-01
  • 1970-01-01
  • 2015-07-16
  • 1970-01-01
相关资源
最近更新 更多