C

时间:2015-10-21 02:43:06

标签: c struct mpi

是否有一种干净的方法可以使用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是否有内置支持?

1 个答案:

答案 0 :(得分:1)

不幸的是,没有简单方式通过MPI传输数据,尽管显然并非如此简单方式。

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

但是,您仍然可以在一些MPI通信中执行此操作,为方便起见,您可以将其包装在一个函数中。但要使它可行,你必须确定一些事情:

  1. 尽量避免malloc遍布各处的数据。 malloc越少越好。除此之外,您可以尝试(如果在代码的上下文中尽可能)尝试分配与您b的所有c和/或struct Foo字段相对应的单个大数据集},并使指针b指向它在这个大数组中的份额。从MPI通信的角度来看,这将更简单,对于您的代码,性能也会更好。
  2. 创建一个空心MPI结构类型,它只包含结构包含的非动态数据,例如a
  3. 然后分两个阶段传输您的数据:首先是结构,然后是包含结构的各个动态字段的各种大型数组。
  4. 最后,在接收端,调整(如果需要)指针以指向新接收的数据。
  5. 这是一个完整的例子,说明了它的工作原理:

    #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
    

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