概述
派生数据类型
原理
目的
- 传递含有不同数据类型值的信息(例如: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} typen−1, d i s p n − 1 disp_{n-1} dispn−1)}
类型图下界:
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} typen−1, d i s p n − 1 disp_{n-1} dispn−1)},其跨度为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} typen−1, d i s p n − 1 disp_{n-1} dispn−1),
( 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} typen−1, d i s p n − 1 + e x disp_{n-1}+ex dispn−1+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(count−1)),…,( t y p e n − 1 type_{n-1} typen−1, d i s p n − 1 + e x ( c o u n t − 1 ) disp_{n-1}+ex(count-1) dispn−1+ex(count−1))}
其类型的跨度为 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
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)}
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} typen−1, d i s p n − 1 disp_{n-1} dispn−1)},其跨度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),...,(typen−1,dispn−1+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),...,(typen−1,dispn−1+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(bl−1)+offset),...,(typen−1,dispn−1+ex(bl−1)+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∗(count−1))}
- 注:
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)={minjdispj不包含lb类型minj{dispj∣typej=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)+ϵ不包含ub类型maxj{dispj∣typej=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笔记:派生数据类型派生数据类型所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复