MPI_Type_create_subarray函数的使用

关键词:MPI自定义数据类型,subarray(子数组)

MPI在高性能计算中应用广泛,并且,MPI开始门槛很低,只要了解并行计算的基本概念和常用MPI函数(MPI_Init( )、MPI_Send( )等)就可以写出并行程序。如果想在此基础上优化程序、或做一些更复杂的计算,就需要用到更多MPI函数或掌握一些更高级的用法。

但同时,MPI本身也是一个非常古老的标准、参考资料有限(特别是中文资料更少),笔者在编写一种新的并行算法时,需要用到MPI_Type_create_subarray( )函数,网络上参考资料有限,因此笔者整理了一些资料和学习笔记,发在博客上,欢迎大家交流讨论。

参考文章

目前通过网络上能够搜到一些MPI_Type_create_subarray()函数的使用方法,但基本上所有的结果无非是把国外网站上的文章Google翻译了一下就贴过来(还丢失了一些东西),价值不大,不如去看原文。

1.stackoverflow: How to use MPI_Type_create_subarray?
2.微软的MPI文档:MPI_Type_create_subarray function

当然,这两篇文章都是英文的,写的略生涩。

MPI_Type_create_subarray( )函数做了什么?

这个函数其实创建了(准确的说是用于创建)一个子数组的自定义MPI数据类型,这个函数本身就做了这件事。其他的事情是配合其他函数一起做的。

为什么使用自定义数据类型?

MPI 的消息收发函数只能处理连续存储的同一类型的数据。不同系统有不同的数据表示格式。MPI预先定义一些基本数据类型,在实现过程中在这些基本数据类型为桥梁进行转换。

自定义数据类型的优势:它的使用可有效地减少消息传递的次数, 增大通信粒度, 并且在收/发消息时避免或减少数据在内存中的拷贝、复制。 所以,我们定义subarray,再进行Send/Revc等消息收发,在一些情况下可以提高效率,这是我们为什么要使用这个函数的原因。

如何使用MPI_Type_create_subarray( )

我们可以在API文档中找到这个函数的定义。

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

但其实真正核心的参数只有3个:

  • bigarray 原来数组的大小
  • subarray 子数组的大小
  • starts 子数组的开始标记

但需要采用数组的方式对参数进行声明,以2维数组(父数组、子数组)为例。

    /* array sizes */
    const int bigsize =10;
    const int subsize =5;
    /*核心参数的定义*/
    int starts[2] = {5,3};
    int subsizes[2]  = {subsize,subsize};
    int bigsizes[2]  = {bigsize, bigsize};

然后就可以使用create函数定义子数组类型了,定义后,我们还需要向MPI提交

/*1.*定义数据类型*/
MPI_Datatype mysubarray;
/*2.子数组创建函数*/
MPI_Type_create_subarray(2, bigsizes, subsizes, starts,
                                 MPI_ORDER_C, MPI_INT, &mysubarray);
/*3.向MPI提交这个数据类型*/
MPI_Type_commit(&mysubarray);

这些步骤都完成后,我们就可以在消息函数中使用这个数据类型,也可以关注一下,是否有效率的提升。

MPI_Send(&(bigarray[0][0]), 1, mysubarray, receiver, ourtag, MPI_COMM_WORLD);
MPI_Recv(&(subarray[0][0]), subsize*subsize, MPI_INT, sender, ourtag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

一个完整的示例程序

#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;
}

运行结果:(我们可以观察什么是子数组)
在这里插入图片描述

参考文献

1.谭光明,C04—数据类型和集合通信.pdf
2.都志辉,高性能计算之并行编程技术—MPI并行程序设计

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值