MPI:每天的数据类型

BACKWARD FORWARD


异构计算要求包含消息的数据是有类型的或者已描述的,使得它的机器的表达可以在计算机体系结构之间转换。MPI系统地描述了消息的数据类型,从简单的原始机器类型到复杂的结构、数组和下标。

MPI消息函数接受一个数据类型参数,它的C typedef是MPI_Datatype:

    MPI_Send(void* buf, int count, MPI_Datatype datatype,
            int dest, int tag, MPI_Comm comm);

基本的数据类型

每个人使用原始的机器数据类型。下面列举了一些C例子(在括号中对应C的数据类型)

    MPI_CHAR (char)
    MPI_INT (int)
    MPI_FLOAT (float)
    MPI_DOUBLE (double)

MPI_Send( )中的计数参数指给定数据类型的元素的个数,而不是字节的总数。

对于消息中包含同构、连续的基本数据类型的数组,这就结束数据类型的讨论。对于消息中包含多于一个数据类型或者它的元素在内存中不连续存放,需要更多的讨论。

步长向量

考虑带有一个2D数组的mesh应用分配到不同的处理器,内部边界行和列在整个网格中北/南和东/西进程之间传送。在C中,一个2D数据的一行的传送是简单的 — 在2D数组中连续的元素向量在数量上等于列数。相反地,单一列元素的存储在内存里是分散的;每一向量元素由一整行的大小与它的前后下标分离。

来自MPI的数据类型是对于非连续数据结构的一个很好的解决方法。代码段推导出一个与这个步长向量匹配的合适的数据类型,然后发送最后一行,如下面所列:

#include <mpi.h>

{
    float               mesh[10][20];
    int                 dest, tag;
    MPI_Datatype        newtype;

/*
 * Do this once.
 */
    MPI_Type_vector(10,         /* # 列元素 */
            1,                  /* 1 列 */
            20,                 /* 跳过20个元素 */
            MPI_FLOAT,          /* 元素是浮点类型 */
            &newtype);          /* MPI 产生的数据类型 */

    MPI_Type_commit(&newtype);
/*
 * 为每一个新消息做此
 */
    MPI_Send(&mesh[0][19], 1, newtype,
            dest, tag, MPI_COMM_WORLD);
}

MPI_Type_commit( )将你确实想要存储和使用的的数据类型与中间类型相分离,此中间类型对于一些非常复杂的数据类型是有效的。

由MPI产生的数据类型的优点是它一旦产生,它们可以重复地使用而不用更多的建立代码。MPI存在许多其它的导出的数据类型构造器。

C 结构

考虑一个图像的应用,它传送8位彩色象素的固定长度扫描线。与象素矩阵一起是扫描线数,一个整数。可以在C中将消息描述成一个结构:

    struct {
        int             lineno;
        char            pixels[1024];
    } scanline;

除了导出类型,对于发送不连续和/或者异构数据消息打包是一个有用的方法。下面列出包装和发送上面结构的代码段:

#include <mpi.h>

{
    unsigned int        membersize, maxsize;
    int                 position;
    int                 dest, tag;
    char                *buffer;
/*
 * Do this once.
 */
    MPI_Pack_size(1,            /* 一个元素 */
            MPI_INT,            /* 数据类型是整数 */
            MPI_COMM_WORLD,     /* 一致的 comm. */
            &membersize);       /* 最大包装空间 req'd */

    maxsize = membersize;
    MPI_Pack_size(1024, MPI_CHAR, MPI_COMM_WORLD, &membersize);
    maxsize += membersize;
    buffer = malloc(maxsize);
/*
 * 为每一新消息做此。
 */
    position = 0;

    MPI_Pack(&scanline.lineno,  /* 包装此元素 */
            1,                  /* 一个元素 */
            MPI_INT,            /* 数据类型 int */
            buffer,             /* 打包缓冲区 */
            maxsize,            /* 缓冲区大小 */
            &position,          /* 下一可用字节的偏移 */
            MPI_COMM_WORLD);    /* 一致的 comm. */

    MPI_Pack(scanline.pixels, 1024, MPI_CHAR,
            buffer, maxsize, &position, MPI_COMM_WORLD);

    MPI_Send(buffer, position, MPI_PACKED,
            dest, tag, MPI_COMM_WORLD);
}

缓冲区分配一次以包含所打包的结构的大小。由于在消息中依赖于实现的开销,必须计算大小。变长的消息可以通过分配能容纳最大可能的消息的缓冲区来处理。对于MPI_Pack( )的位置参数总是返回当前包装缓冲区的大小。

下面列出解包消息的代码段,假设已分配接受缓冲区。

{
    int             src;
    int             msgsize;
    MPI_Status      status;

    MPI_Recv(buffer, maxsize, MPI_PACKED,
            src, tag, MPI_COMM_WORLD, &status);

    position = 0;
    MPI_Get_count(&status, MPI_PACKED, &msgsize);

    MPI_Unpack(buffer,          /* 打包缓冲区 */
            msgsize,            /* 缓冲区大小 */
            &position,          /* 下一元素的字节偏移 */
            &scanline.lineno,   /* 解包这个元素 */
            1,                  /* 一个元素 */
            MPI_INT,            /* 数据类型 int */
            MPI_COMM_WORLD);    /* 一致的 comm. */

    MPI_Unpack(buffer, msgsize, &position,
            scanline.pixels, 1024, MPI_CHAR, MPI_COMM_WORLD);
}

你应该能修改上面的代码以适应任意的结构。完全可能改变要解包元素的个数,这基于同一消息中前面解包的应用信息。

  • LAM / MPI 并行计算/ Ohio Supercomputer Center / [email protected]

  • Copyright: NPACT BACKWARD FORWARD