如何使用MPI_Type_create_subarray?

20

很明显它的参数是:

int MPI_Type_create_subarray(
  int ndims,
  int array_of_sizes[],
  int array_of_subsizes[],
  int array_of_starts[],
  int order,
  MPI_Datatype oldtype,
  MPI_Datatype *newtype
);

然而,我不明白这个方法如何接收我们想要拆分的原始数组以及在哪里返回新的子数组(因为这个方法应该返回一个整数)。换句话说,我只想看到C++中这个方法的简单实现,但我在网上找不到。

1个回答

38

MPI_Type_create_subarray()并不接收原始数组,也不返回子数组;它创建一个MPI类型,描述给定类型的大数组中子数组的存储布局,需要提供一组子数组大小和一个起始点。

您可以使用这个新创建的MPI类型从任何适当大小的数组中提取所需的数据,并将其发送到另一个任务(通过点对点消息传递例程)、所有其他任务(通过集合传递),或将其写入磁盘(使用MPI-IO)。在下面的示例中,rank 0 使用一个MPI子数组类型从一个整数的大数组中提取一个子数组,并将其发送到rank 1。 接收到这个数据的rank 1 只需将其作为若干个整数接收,而无需特别地接收它。

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

void printarr(int **data, int n, char *str);
int **allocarray(int n);

int main(int argc, char **argv) {

    /* array sizes */
    const int bigsize =10;
    const int subsize =5;

    /* communications parameters */
    const int sender  =0;
    const int receiver=1;
    const int ourtag  =2;

    int rank, size;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    if (size < receiver+1) {
        if (rank == 0)
            fprintf(stderr,"%s: Needs at least %d  processors.\n", argv[0], receiver+1);
        MPI_Finalize();
        return 1;
    }

    if (rank == sender) {
        int **bigarray = allocarray(bigsize);
        for (int i=0; i<bigsize; i++)
            for (int j=0; j<bigsize; j++)
                bigarray[i][j] = i*bigsize+j;


        printarr(bigarray, bigsize, " Sender: Big array ");

        MPI_Datatype mysubarray;

        int starts[2] = {5,3};
        int subsizes[2]  = {subsize,subsize};
        int bigsizes[2]  = {bigsize, bigsize};
        MPI_Type_create_subarray(2, bigsizes, subsizes, starts,
                                 MPI_ORDER_C, MPI_INT, &mysubarray);
        MPI_Type_commit(&mysubarray);

        MPI_Send(&(bigarray[0][0]), 1, mysubarray, receiver, ourtag, MPI_COMM_WORLD);
        MPI_Type_free(&mysubarray);

        free(bigarray[0]);
        free(bigarray);

    } else if (rank == receiver) {

        int **subarray = allocarray(subsize);

        for (int i=0; i<subsize; i++)
            for (int j=0; j<subsize; j++)
                subarray[i][j] = 0;

        MPI_Recv(&(subarray[0][0]), subsize*subsize, MPI_INT, sender, ourtag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

        printarr(subarray, subsize, " Receiver: Subarray -- after receive");

        free(subarray[0]);
        free(subarray);
    }

    MPI_Finalize();
    return 0;
}

void printarr(int **data, int n, char *str) {    
    printf("-- %s --\n", str);
    for (int i=0; i<n; i++) {
        for (int j=0; j<n; j++) {
            printf("%3d ", data[i][j]);
        }
        printf("\n");
    }
}

int **allocarray(int n) {
    int *data = malloc(n*n*sizeof(int));
    int **arr = malloc(n*sizeof(int *));
    for (int i=0; i<n; i++)
        arr[i] = &(data[i*n]);

    return arr;
}

运行此命令会得到以下结果

$ mpicc -o subarray subarray.c  -std=c99 -Wall -g
$ mpirun -np 2 ./subarray
    --  Sender: Big array  --
  0   1   2   3   4   5   6   7   8   9 
 10  11  12  13  14  15  16  17  18  19 
 20  21  22  23  24  25  26  27  28  29 
 30  31  32  33  34  35  36  37  38  39 
 40  41  42  43  44  45  46  47  48  49 
 50  51  52  53  54  55  56  57  58  59 
 60  61  62  63  64  65  66  67  68  69 
 70  71  72  73  74  75  76  77  78  79 
 80  81  82  83  84  85  86  87  88  89 
 90  91  92  93  94  95  96  97  98  99 
--  Receiver: Subarray -- after receive --
 53  54  55  56  57 
 63  64  65  66  67 
 73  74  75  76  77 
 83  84  85  86  87 
 93  94  95  96  97

3
感谢您提供的良好示例。使用MPI_Sendrecv可以省略掉一些代码。 - Hristo Iliev
我发现新用户在使用sendrecv()自己发送和接收时会感到困惑,但是引入非阻塞通信也许并不比这更好。最清晰的方法可能是直接向另一个进程发送。我现在将其保留,但如果它引起问题,我会采取其他措施。 - Jonathan Dursi
是的,你可以(当然可以)使用mpi_type_create_subarray来发送和接收边界区域/幽灵行。你需要创建子数组类型来描述你要发送给上/下/左/右邻居的“真实”更新数据,并描述你要接收到的幽灵行,然后像上面的示例一样发送和接收它们。但你仍然需要进行发送和接收操作。你创建的子数组类型不会进行任何通信,也不会生成任何指针或其他内容;它只是描述了一个内存布局,使你能够直接将数据发送/接收到你的网格中。 - Jonathan Dursi
@JonathanDursi 对不起再次打扰您,但我不明白如何进行发送和接收。我的意思是,我应该从“主”排名(可能为0)向其他排名发送块矩阵(可能通过if...else结构),然后将结果接收到主机吗? - Pippo
1
@RestlessC0bra 是的。在标准中,类型的一致性意味着数据的数量和底层类型(例如,MPI_INT)必须相同,但布局可以不同。 - Jonathan Dursi
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接