新类型的构造和使用

  通常情况下,新类型的构造和使用由下面几个过程(依次)组成:

  1. 新类型的构造(通过调用构造器)
  2. 新类型的提交(调用MPI_TYPE_COMMIT)
  3. 新类型的使用(发送,接收消息等等)
  4. 新类型的释放(调用MPI_TYPE_FREE)

  提交新类型的调用形式如下:

  MPI_TYPE_COMMIT( datatype )
  INOUT datatype

  int MPI_Type_commit( MPI_Datatype *datatype );
  一旦一个新的数据类型经过提交,用户可以在程序中向使用预定义类型一样的使用它们,这也使用户可以在自己定义的派生数据类型的基础上,再定义更为复杂的新的类型。 释放新类型的调用形式如下:

  MPI_TYPE_FREE( datatype )
  INOUT datatype

  int MPI_Type_free( MPI_Datatype *datatype );

  打包和解包
  打包(pack)和解包(unpack)为用户提供了发送不连续数据又一种方法。基本原理是在发送前先把数据包装(拷贝)到一个连续的(发送)缓冲区,数据到达后再从(连续得接收)缓冲区内把数据取出来(解包)。
与打包/解包操作有关的函数调用如下。

  打包操作

  MPI_PACK( inbuf, incount, datatype, outbuf, outcount, position, comm )
  IN inbuf 输入缓冲区起始地址(需要打包的数据)
  IN incount 输入数据的数目
  IN datatype 输入数据的类型
  OUT outbuf 输出缓冲区起始地址(打包后的数据)
  IN outcount 输出缓冲区的长度
  INOUT position 输出缓冲区当前打包位置
  IN comm 通信域

  int MPI_Pack( void *inbuf, int incount, MPI_Datatype datatype,
  void *outbuf, int outcount, int *position, MPI_Comm comm );

  需要指出的是,一个完整的打包过程通常包括连续多次对MPI_PACK的调用。

  解包操作

  MPI_UNPACK( inbuf, insize, position, outbuf, outcount, datatype, comm )
  IN inbuf 输入缓冲区起始地址(需要解包的数据)
  IN insize 输入数据的数目
  INOUT position 输入缓冲区当前解包位置
  OUT outbuf 输出缓冲区起始地址(打包后的数据)
  IN outcount 输出缓冲区的长度
  IN datatype 输出数据的类型
  IN comm 通信域

  int MPI_Unpack( void *inbuf, int insize, int *position, void *outbuf,
  int outcount, MPI_Datatype datatype,MPI_Comm comm );

  需要指出的是,一个完整的解包过程也通常包括连续多次对MPI_UNPACK的调用。

  计算打包所需的空间

  MPI_PACK_SIZE( incount, datatype, comm, size )
  IN incount 给定数据类型的数目
  IN datatype 数据类型
  IN comm 通信域
  OUT size 以字节为单位的incount个datatype类型数据打包所需要的空间

  int MPI_Pack_size( int incount, MPI_Datatype datatype, MPI_Comm comm,
int *size );
  注意size返回的是一个上界,因为打包空间可能会依赖于上下文(比如第一个打包单元需要更多的空间)。

  下面是一个完整的打包和解包的例子(在都志辉等所著的《高性能计算并行编程技术--MPI并行程序设计》中的173页给出)。

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

  int main( int argc, char **argv) {
  int rank, packsize, position;
  int a;
  double b;
  char packbuf[100];

  MPI_Init( &argc, &argv );
  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
  
  if( rank == 0 ) {
   // 进程0读数据
   scanf( "%d%lf", &a, &b );
   packsize = 0;
   MPI_Pack( &a, 1, MPI_INT, packbuf, 100, &packsize,
   MPI_COMM_WORLD );
   MPI_Pack( &b, 1, MPI_DOUBLE, packbuf, 100, &packsize,
   MPI_COMM_WORLD );
  }
  //
  MPI_Bcast( &packsize, 1, MPI_INT, 0, MPI_COMM_WORLD );
  //
  MPI_Bcast( packbuf, packsize, MPI_PACKED, 0, MPI_COMM_WORLD );
  if( rank != 0 ) {
   position = 0;
   MPI_Unpack( packbuf, packsize, &position, &a, 1, MPI_INT,
   MPI_COMM_WORLD );
   MPI_Unpack( packbuf, packsize, &position, &b, 1, MPI_DOUBLE,
   MPI_COMM_WORLD );
  }

  printf( "Process %d got %d and %lf \n", rank , a, b );
  
  MPI_Finalize( );
  return 0;
  }