我是靠谱客的博主 沉静大山,最近开发中收集的这篇文章主要介绍MPI笔记:派生数据类型派生数据类型,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

派生数据类型

原理

目的

  • 传递含有不同数据类型值的信息(例如:int-float)
  • 传递非连续数据的用户(例如:矩阵的一个子块)

方法

  • 在发送端将非连续的数据打包到一个连续的缓冲区,在接收端再解包(缺点:接收端和发送端都需要额外的内存和内存拷贝操作)

类型图——通用的数据类型表述方法

  • 类型图:是一系列二元数组的集合,两个数据类型是否相同,取决于它们的类型图是否相同。将类型图和一个数据缓冲区的基地值结合起来,可以说明一个通信缓冲区的数据分布情况。

类型图={<基类型0, 偏移0>, <基类型1, 偏移1>, <基类型2, 偏移2>,…,<基类型n-1, 偏移n-1>}
基类型:指出该类型图中包含哪些基本的数据类型
偏移:指该基类型在整个类型图中的起始位置
例子:MPI_INT := {(int, 0)}

在这里插入图片描述

  • 类型图的相关量:

类型图:
typemap={( t y p e 0 type_0 type0, d i s p 0 disp_0 disp0),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 disp_{n-1} dispn1)}
类型图下界:
lb(typemap)=min{ d i s p j disp_j dispj}, 0 ≤ leq j ≤ leq n-1
类型图上界:
ub(typemap)=max{ d i s p j disp_j dispj+sizeof( t y p e j type_j typej)}, 0 ≤ leq j ≤ leq n-1
类型图的跨度:
extent(typemap)=ub(typemap)-lb(typemap)+ ϵ epsilon ϵ
ϵ epsilon ϵ: 由于不同的类型有不同的对其位置要求,是能够使类型图的跨度,满足类型图中所有的类型能达到下一个对齐要求,所需要的最小非负整数值
例子:
type={(double,0),(char,8)};假设double型的值必须严格分配到地址为8的倍数的存储空间,则该数据类型的extent是16

新数据类型的定义

连续复制的类型生成

  • 数据类型生成器:int MPI_Type_contiguous(int count,MPI_Datatype oldtype,MPI_Datatype *newtype)

IN count:复制个数(非负整数)
IN oldtype:旧数据类型(句柄)
OUT newtype:新数据类型(句柄)
RETURN int:返回1,0标志操作是否成功的

  • 例子:

    oldtype={(double,0),(char,8)}
    extent=16
    count=3
    MPI_TYPE_CONTIGUOUS返回的新的数据类型为:
    newtype={(double,0),(char,8),(double,16),(char,24),(double,32),(char,40)}

在这里插入图片描述

  • 更通用的表达:
    设旧类型oldtype的类型图为
    oldtype={( t y p e 0 type_0 type0, d i s p 0 disp_0 disp0),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 disp_{n-1} dispn1)},其跨度为extent=ex.
    新类型newtype将oldtype连续复制count次,则新类型newtype的类型图为

newtype={( t y p e 0 type_0 type0, d i s p 0 disp_0 disp0),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 disp_{n-1} dispn1),
( t y p e 0 type_0 type0, d i s p 0 + e x disp_0+ex disp0+ex),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 + e x disp_{n-1}+ex dispn1+ex),
…,
( t y p e 0 type_0 type0, d i s p 0 + e x ( c o u n t − 1 ) disp_0+ex(count-1) disp0+ex(count1)),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 + e x ( c o u n t − 1 ) disp_{n-1}+ex(count-1) dispn1+ex(count1))}
其类型的跨度为 c o u n t × e x count times ex count×ex

向量数据类型的生成

  • 数据类型生成器1:int MPI_Type_vector(int count,int blocklength,int stride,MPI_Datatype oldtype,MPI_Datatype *newtype)

IN count:块的数目(非负整数)
IN blocklength:每个块中所含元素的个数(非负整数)
IN stride:各块第一个元素之间相隔的元素个数(整数)
IN oldtype:旧数据类型(句柄)
OUT newtype:新数据类型(句柄)

  • 数据类型生成器2:int MPI_Type_hvector(int count,int blocklength,int stride,MPI_Datatype oldtype,MPI_Datatype *newtype)

IN count:块的数目(非负整数)
IN blocklength:每个块中所含元素的个数(非负整数)
IN stride:各块第一个元素之间相隔的字节数(整数)
IN oldtype:旧数据类型(句柄)
OUT newtype:新数据类型(句柄)

  • 例子:

    oldtype={(double,0),(char,8)},extent=16
  1. MPI_TYPE_VECTOR(2,3,4,oldtype,newtype)生成的数据类型的类型图为:
    newtype={(double,0),(char,8),(double,16),(char,24),(double,32),(char,40),(double,64),(char 72),(double,80),(char,88),(double,96),(char,104)}

在这里插入图片描述

  1. MPI_TYPE_VECTOR(3,1,-2,oldtype,newtype)生成的数据类型的类型图为:
    newtype={(double,0),(char,8),(double,-32),(char,-24),(double,-64),(char,-56)}
  • 更通用的表达:
    假设oldtype={( t y p e 0 type_0 type0, d i s p 0 disp_0 disp0),…,( t y p e n − 1 type_{n-1} typen1, d i s p n − 1 disp_{n-1} dispn1)},其跨度extent=ex.
    设blocklength=bl,新建的数据类型有count*bl个入口项,类型图为:

block(offset)= { ( t y p e 0 , d i s p 0 + o f f s e t ) , . . . , ( t y p e n − 1 , d i s p n − 1 + o f f s e t ) , {(type_0,disp_0+offset),...,(type_{n-1},disp_{n-1}+offset), {(type0,disp0+offset),...,(typen1,dispn1+offset),
( t y p e 0 , d i s p 0 + e x + o f f s e t ) , . . . , ( t y p e n − 1 , d i s p n − 1 + e x + o f f s e t ) , (type_0,disp_0+ex+offset),...,(type_{n-1},disp_{n-1}+ex+offset), (type0,disp0+ex+offset),...,(typen1,dispn1+ex+offset),
. . . , ..., ...,
( t y p e 0 , d i s p 0 + e x ( b l − 1 ) + o f f s e t ) , . . . , ( t y p e n − 1 , d i s p n − 1 + e x ( b l − 1 ) + o f f s e t ) } (type_0,disp_0+ex(bl-1)+offset),...,(type_{n-1},disp_{n-1}+ex(bl-1)+offset)} (type0,disp0+ex(bl1)+offset),...,(typen1,dispn1+ex(bl1)+offset)}
newtype= { b l o c k ( 0 ) , b l o c k ( s t r i d e ) , . . . , b l o c k ( s t r i d e ∗ ( c o u n t − 1 ) ) } {block(0),block(stride),...,block(stride*(count-1))} {block(0),block(stride),...,block(stride(count1))}

  • 注:MPI_TYPE_CONTIGUOUS(count,oldtype,newtype)等价于MPI_TYPE_VECTOR(count,1,1,oldtype,newtype)MPI_TYPE_VECTOR(1,count,n,oldtype,newtype)(n为升序)

索引数据类型的生成

  • 类型生成器1:int MPI_Type_indexed(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements, MPI_Datatype_oldtype, MPI_Datatype *newtype)

IN count:块的数量(整型)
IN array_of_blocklengths:每个块中所含的元素个数(非负整数数组)
IN array_of_displacements:各块偏移的元素个数(整数数组)
IN oldtype:旧数据类型
OUT newtype:新数据类型

  • 类型生成器2:int MPI_Type_hindexed(int count, int *array_of_blocklengths, int *array_of_displacements, MPI_Datatype_oldtype, MPI_Datatype *newtype)

IN count:块的数量(整型)
IN array_of_blocklengths:每个块中所含的元素个数(非负整数数组)
IN array_of_displacements:各块偏移的字节数(整数数组)
IN oldtype:旧数据类型
OUT newtype:新数据类型

  • 例子:
    设oldtype= { ( d o u b l e , 0 ) , ( c h a r , 8 ) } {(double,0),(char,8)} {(double,0),(char,8)},extent=16,B=(3,1),D=(4,0),
    MPI_TYPE_INDEXED(2,B,D,oldtype,newtype)调用生成的数据类型图为:
    newtype={(double,64),(char,72),(double,80),(char,88),(double,96),(char,104),(double,0),(char,8)}

在这里插入图片描述

结构数据类型

  • 类型生成器:int MPI_Type_struct(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements, MPI_Datatype array_of_types, MPI_Datatype *newtype)

IN count:块的数目(整数)
IN array_of_blocklengths:每个块中所含元素个数(非负整数数组)
IN array_of_displacements:各块偏移字节数(整数数组)
IN array_of_types:每个块中元素的类型(句柄)
OUT newtype:新数据类型(句柄)

  • 例子:
    设type1={(double,0),(char,8)},extent=16,B=(2,1,3),D=(0,16,26),T=(MPI_FLOAT,type1,MPI_CHAR).则MPI_TYPE_STRUCT(3,B,D,T,newtype)返回的新数据类型为:
    newtype={(float,0),(float,4),(double,16),(char,24),(char,26),(char,27),(char,28)}

在这里插入图片描述

  • 注:MPI_TYPE_HINDEXED(count,B,D,oldtype,newtype)等价于MPI_TYPE_STRUCT(count,B,D,T,newtype),其中T的每一项都是oldtype

新类型的交替和释放

新定义的数据类型在使用之前,必须先递交给MPI系统.递交之后的数据类型可以作为一个基本数据类型,用在数据类型生成器中产生新的数据类型.

  • 递交操作:int MPI_Type_commit(MPI_Datatype *datatype)

递交作用用于定义新的数据类型

  • 释放操作:int MPI_Type_free(MPI_Datatype *datatype)

释放操作用于释放已提交的数据类型,并且将该数据类型的指针或句柄设为空MPI_DATATYPE_NULL.由该派生类型定义的新派生类型不受当前派生类型释放的影响.

与数据类型相关的调用

  • int MPI_Type_extent(MPI_Datatype datatype, int *extent)

IN datatype:数据类型(句柄)
OUT extent:数据类型的extent(整型)
作用:MPI_TYPE_EXTENT以字节为单位返回一个数据类型的跨度extent

  • int MPI_Type_size(MPI_Datatype datatype, int *size)

IN datatype:数据类型(句柄)
OUT size:数据类型的大小(整型)
作用:MPI_TYPE_SIZE以字节为单位,返回一个数据类型有用部分所占空间的大小,即跨度减去类型中空隙后的空间的大小.

  • int MPI_Get_elements(MPI_Status status, MPI_Datatype datatype, int *count)

IN status:接受操作后返回的状态(状态类型)
IN datatype:接受操作后使用的数据类型(句柄)
OUT count:接收到的基本元素个数
作用:MPI_GET_ELEMENTS返回以基本数据类型为单位的个数

  • int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *int)

IN status:接受操作后返回的状态(状态类型)
IN datatype:接受操作后使用的数据类型(句柄)
OUT count:接受到的以指定的数据类型为单位的数据个数(整型)
MPI_GET_COUNT返回的是以指定的数据类型为单位,接受操作接收到的数据个数.

下界标记类型和上界标记类型

上下界标记类型的定义

MPI提供两个特殊的数据类型,称为伪数据类型,上界标记类型MPI_UB和下界标记类型MPI_LB.这两个数据类型不占空间,即extent(MPI_LB)=extent(MPI_UB)=0.

  • typemap的下界: l b ( T y p e m a p ) = { m i n j d i s p j 不 包 含 l b 类 型 m i n j { d i s p j ∣ t y p e j = l b } 若 含 有 l b 类 型 lb(Typemap) = begin{cases} min_j disp_j quad不包含lb类型 \ min_j {disp_j | type_j=lb} quad 若含有lb类型 end{cases} lb(Typemap)={minjdispjlbminj{dispjtypej=lb}lb

  • typemap的上界: u b ( T y p e m a p ) = { m a x j d i s p j + s i z e o f ( t y p e j ) + ϵ 不 包 含 u b 类 型 m a x j { d i s p j ∣ t y p e j = u b } 若 含 有 u b 类 型 ub(Typemap) = begin{cases} max_j disp_j+sizeof(type_j)+epsilon quad不包含ub类型 \ max_j {disp_j | type_j=ub} quad 若含有ub类型 end{cases} ub(Typemap)={maxjdispj+sizeof(typej)+ϵubmaxj{dispjtypej=ub}ub

  • 数据类型typemap的跨度:
    e x t e n t ( t y p e m a p ) = u b ( t y p e m a p ) − l b ( t y p e m a p ) extent(typemap)=ub(typemap)-lb(typemap) extent(typemap)=ub(typemap)lb(typemap)

上下界标记类型的定义函数

  • int MPI_Type_lb(MPI_Datatype datatype, int *displacement)

IN datatype:数据类型(句柄)
OUT displacement:下界的偏移(整数)

  • int MPI_Type_ub(MPI_Datatype datatype, int *displacement)

IN datatype:数据类型(句柄)
OUT displacement:上界的位移(整数)

  • 例子:
    D=(-3,0,6);T=(MPI_LB,MPI_INT,MPI_UB);B=(1,1,1),则MPI_TYPE_STRUCT(3,B,D,T,type1)产生了一个extent=9的数据类型
    type1={(lb,-3),(int,0),(ub,6)}.如果该数据类型被MPI_TYPE_COUNTIGUOUS(2,type1,type2)复制两次,则新生成的序列为
    type2={(lb,3),(int,0),(int,9),(ub,15)}
    (如果lb或者ub出现在数据类型两端以外的位置,则它们可以被忽略)

打包和解包

打包和解包操作是为了发送不连续的数据,在发送前显式地把数据包装到一个连续的缓冲区,在接收之后从连续缓冲区中解包.

  • 打包:
    int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outcount, int *position, MPI_Comm comm)

IN inbuf:输入缓冲区起始地址
IN incount:输入数据项个数
IN datatype:每个输入数据项的类型
OUT outbuf:输出缓冲区开始地址
IN outcount:输出缓冲区的大小
INOUT position:缓冲区当前位置
IN comm:通信域

  • 解包:
    int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm)

IN inbuf:输入缓冲区起始
IN insize:输入数据项数目
INOUT position:缓冲区当前位置
OUT outbuf:输出缓冲区起始
IN outcount:输出缓冲区大小
IN datatype:每个输入数据项的类型
IN comm:打包的信息的通信域

  • 打包数据所需的空间:
    int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)

IN incount:指定数据类型的个数
IN datatype:数据类型
IN comm:通信域
OUT size:以字节为单位,incount个datatype数据类型打包需要的空间

  • 例子:
int position, i, j, a[2];
char buff[1000];
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
// 进程0发送信息
if(myrank==0){
   position=0; // 打包的起始位置
   MPI_Pack(&i, 1, MPI_INT, buff, 1000, &position, MPI_COMM_WORLD);// 将整数i打包
   MPI_Pack(&j, 1, MPI_INT, buff, 1000, &position, MPI_COMM_WORLD);
   // 将整数j打包
   MPI_Send(buff, position, MPI_PACKED, 1, 0, MPI_COMM_WORLD);
   // 将打包后的数据发送出去
}else if(myrank==1){
    MPI_Recv(a, 2, MPI_INT, 0, 0, MPI_COMM_WORLD);
    // 以整型从进程0接收消息
}

参考文献:《MPI并行程序设计》链接: https://pan.baidu.com/s/1mVKWmzc8AHT1Crxa5ZWArQ 密码: c49v

最后

以上就是沉静大山为你收集整理的MPI笔记:派生数据类型派生数据类型的全部内容,希望文章能够帮你解决MPI笔记:派生数据类型派生数据类型所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部