📄 mpi312.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE> ICPSEP Content
</TITLE>
<META NAME="GENERATOR" CONTENT="Mozilla/3.0Gold (X11; I; SunOS 5.4 sun4m) [Netscape]">
</HEAD>
<BODY BGCOLOR="#F0F8FF">
<TABLE WIDTH="100%" >
<TR>
<TD align=left><FONT SIZE=+2>3.12 派生数据类型(derived datatypes)</FONT>
</TD>
<TD align=right><A HREF="mpi310.htm" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi310.htm"><IMG SRC="backward.gif" tppabs="http://arch.cs.pku.edu.cn/image/backward.gif" ALT="BACKWARD" HEIGHT=32 WIDTH=32></A><A HREF="mpi313.htm" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi313.htm"><IMG SRC="forward.gif" tppabs="http://arch.cs.pku.edu.cn/image/forward.gif" ALT="FORWARD" HEIGHT=32 WIDTH=32></A>
</TD>
</TR>
</TABLE>
<P>
<HR WIDTH="100%"></P>
<P>到此为止,所有的点对点通信只牵涉含有相同数据类型的相邻缓冲区.这对两种用户限制太大.一种是经常想传送含有不同数据类型值的消息的用户(例如,一个整数计数值跟着一些实数);另一种是经常发送非连续数据的用户(例如发送矩阵的一个子块).一种解决的办法是在发送端把非连续的数据打包到一个连续的缓冲区,在接收端再解包.这样做的缺点在于在两端都需要额外的内存到内存拷贝操作,甚至当通信子系统具有收集分散数据功能的时候也是如此.而MPI提供说明更通用的,混合的非连续通信缓冲区的机制.直到执行(implementation)时再决定数据应该在发送之前打包到连续缓冲中,还是直接从数据存储区收集.
</P>
<P>这里提供的通用机制允许不需拷贝,而是直接传送各种形式和大小的目标.我们并没有假设MPI库是用本地语言描述的连续目标.因此,如果用户想要传送一个结构或一个数组部分,则需要向MPI提供一个通信缓冲区的定义,该定义用问题模仿那个结构和数组部分的定义.这些工具可以用于使库设计者定义能够传送用本地语言定义的目标的通信函数:通过对可获得的符号表或虚拟向量(dope
vector)的定义解码即可. 这种高级通信功能不是MPI的部分. </P>
<P>更通用的通信缓冲区可以通过用本节描述的生成器从基本数据类型中构造的派生数据类型来替换前面使用的基本数据类型来说明.这些构造派生数据类型的方法可以被应用多次.
</P>
<P>一个通用的数据类型是一个模糊的目标,它说明两件事情: </P>
<UL>
<P>一个基本数据类型序列; </P>
<P>一个整数(字节)偏移(displacements)序列 </P>
</UL>
<P>该偏移不要求是整数,互相不同,或者升序.因此,各项的顺序可以和存储的顺序不一致,并且同样的项可以出现多次.这样的序列我们称之为"类型映像"(type
map).基本类型序列(忽略偏移)是该数路类型的“类型鉴字"(type signature).
</P>
<P>令</P>
<UL>
<P>typemap={(type0,disp0),...,(typen-1,dispn-1)}, 是这样的类型映射, 其typei是基本类型,
dispi是偏移. </P>
</UL>
<P>令 </P>
<UL>
<P>typesig={type,...,typen-1} 是相关的类型签字. 这个类型映射和一个基地址buf共同说明一个通信缓冲区:该通信缓冲区包含n个项,
第 i 项在地址buf+dispi处,类型为typei. 一个从该通信缓冲区集成的消息将含有n个值,其类型由typesig定义.
</P>
</UL>
<P>我们可以对通用数据类型使用一个句柄作为发送或接收操作的参数,来代替用基本数据类型做参数.MPI_SEND(buf,1,datatype,...)操作将用以buf作为基地址的发送缓冲区和以datatype相关的通用数据类型;它将产生一个带有由datatype参数决定的类型签名的消息.MPI_RECV(buf,1,datatype,...)将使用buf定义的基地址作为接收缓冲区地址和datatype相关的通用数据类型.
</P>
<P>通用数据类型可以用在所有的发送和接收操作中.我们在3.12.5中讨论参数count>1的情况.
</P>
<P>3.2.2中提供的基本数据类型是通用数据类型的特例,他们已经被预先定义好了.这样看来,MPI_INIT是一个预先定义好了的数据类型句柄,其类型映像为{(int
, 0)},有一个类型入口项int和偏移0. 其它的基本数据类型与此相似. </P>
<P>数据类型的扩充(extent)被定义为该数据类型获得的入口项中从第一个类型到最后一个类型间的距离,循环满足分配的要求.
即如果 </P>
<UL>
<P>typemap={(type0,disp0),...,(typen-1,disp n-1)}, </P>
</UL>
<P>则 </P>
<UL>
<P>lb(typemap)=min dispJ, </P>
<P>j </P>
<P>ub(typemap)=max(dispJ+sizeof(typeJ)), </P>
<P>j </P>
<P>extent(typemap)=ub(typemap)-lb(typemap)+e (3.1) </P>
</UL>
<P>如果typei 要求分配ki倍数的字节地址, 则e是使extent(typemap)进入(rount)下一个maxiki所需要的最小非负增长.
</P>
<P>例3.18 假设type={(double,0),(char,8)}(一个double型的值在偏移0,后面在偏移8处跟一个字符值).进一步假设double型的值必须严格分配到地址为8的倍数的存储空间.则该数据类型的extent是16(从9循环到下一个8的倍数).一个由一个字符或面紧跟一个双精度值的数据类型,其extent也是16.
</P>
<P>##理论基础## </P>
<P>extent的定义是由这样一个假设激发的:在每个数组结构末尾添加的"充塞"(padding)数量是满足分配限制所要求的最小值.在3.12.3中提供了对extent更明确的控制.这种精确控制是为了适应上述假设不成立的情况,例如使用了union类型.
</P>
<P><A NAME="3.12.1"></A></P>
<P>3.12.1 数据类型生成器 </P>
<P>Contiguous(连续) 最简单的数据类型生成器是MPI_TYPE_CONTIGUOUS,`允许把一个数据类型复制到连续的位置.
</P>
<P><TT>MPI_TYPE_CONTIGUOUS(count,oldtype,newtype) </TT></P>
<UL>
<P><TT>IN count 复制个数(非负整数) </TT></P>
<P><TT>IN oldtype 旧数据类型(句柄) </TT></P>
<P><TT>OUT newtype 新数据类型(句柄) </TT></P>
</UL>
<P><TT>int MPI_Type_contiguous(int count,MPI_Datatype oldtype, MPI_Datatype
*newtype) </TT></P>
<P><TT>MPI_TYPE_CONTIGUOUS(COUNT,OLDTYPE,NEWTYPE,IERROR) </TT></P>
<UL>
<P><TT>INTEGER COUNT,OLDTYPE,NEWTYPE,IERROR </TT></P>
</UL>
<P>newtype 是连接count份oldtype类型数据所获得的数据类型.连接由使用extent作为连接拷贝的大小来定义的.
</P>
<P>例3.19令oldtype的类型映像为</P>
<UL>
<P>{(doubel,0),(char,8)},extent=16,count=3,</P>
</UL>
<P>则newtype返回的类型映像为 </P>
<UL>
<P>{(double,0),(char,8),(double,16),(char,24),(double,32),(char,40)}; </P>
</UL>
<P>double和char类型元素的偏移被改为 08,16,24,32,40. </P>
<P>一般地,假设oldtype的类型映像为 </P>
<UL>
<P>{(type0, disp0), (typen-1,dispn-1 )}, </P>
</UL>
<P>extent = ex. 则newtype具有count.n个入口项,类型映像为 </P>
<UL>
<P>{(type0, disp0),..., (typen-1,dispn-1 ), (type0, disp0+ex),..., (typen-1,
dispn-1+ex), ..., (type0, disp0+ex.(count-1)),..., (typen-1, dispn-1+ex.(count-1))}.
</P>
</UL>
<P>Vector(向量) MPI_TYPE_VECTOR是一个更通用的生成器,允许复制一个数据类型到含有相等大小块的空间.每个块通过连接相同数量的旧数据类型的拷贝来获得.块与块之间的空间是旧数据类型的extent的倍数.
</P>
<P><TT>MPI_TYPE_VECTOR(count,blocklength,stride,oldtype,newtype) </TT></P>
<UL>
<P><TT>IN count 块的数量(非负整数) </TT></P>
<P><TT>IN blocklength 每个块中所含元素个数(非负整数) </TT></P>
<P><TT>IN stride 各块第一个元素之间相隔的元素个数(整数) </TT></P>
<P><TT>IN oldtype 旧数据类型(句柄) </TT></P>
<P><TT>OUT newtypr 新数据类型(句柄) </TT></P>
</UL>
<P><TT>int MPI_Type_vector(int count,int blocklength,int stride,] MPI_Datatype
oldtype,MPI_Datatype *newtype) </TT></P>
<P><TT>MPI_TYPE_VECTOR(COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,IERROR)</TT></P>
<UL>
<P><TT>INTEGER COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,IERROR </TT></P>
</UL>
<P>例3.20 再一次假设oldtype的类型映像为{(double,0),(char,8)},extent=16.则MPI_TYPE_VECTOR(2,3,4,oldtype,newtype)调用生成的数据类型将是以下的类型映像:
</P>
<UL>
<P>{(double,0),(char,8), (double,16),(char,24), (double,32),(char,40),
(double,64),(char,72), (double,80),(char,88),(double,96),(char,104)}. </P>
</UL>
<P>即两个块,每个旧类型有三个拷贝,相邻块之间的步长stride为4个元素. </P>
<P>例 3.21 调用MPI_TYPE_VECTOR(3,1,-2,oldtype,newtype)将生成以下的数据类型:
</P>
<UL>
<P>{(double,0),(char,8),(double,-32),(char,-24),(double,-64),(char,-56)}.
</P>
</UL>
<P>一般地,假设oldtype的类型映像为 </P>
<UL>
<P>{(type0, disp0), (typen-1,dispn-1 )}, </P>
</UL>
<P>extent = ex. 设bl为blocklength.新创建的数据类型有count.bl个入口项,类型映像为
</P>
<UL>
<P>{(type0, disp0),..., (typen-1,dispn-1 ), </P>
<P>(type0, disp0+ex),..., (typen-1, dispn-1+ex),..., </P>
<P>(type0, disp0+ex.(bl-1)),..., (typen-1, dispn-1+ex.(bl-1)), </P>
<P>(type0, disp0+ex.stride),..., (typen-1, dispn-1+ex.stride),..., </P>
<P>(type0, disp0+ex.(stride+bl-1)),..., (typen-1, dispn-1+ex.(stride+bl-1)),...,
</P>
<P>(type0, disp0+ex.(count-1)),..., (typen-1, dispn-1+ex.(count-1)),...,
</P>
<P>(type0, disp0+ex.(count-1).stride),..., </P>
<P>(typen-1, dispn-1+ex.(stride.(count-1)+bl-1)},..., </P>
<P>(typen-1, dispn-1+ex.(stride.(count-1)+bl-1)}. </P>
</UL>
<P>MPI_TYPE_CONTIGUOUS( count, oldtype, newtype )调用等价于调用MPI_TYPE_VECTOR(
count, 1, 1, oldtype, newtype ), 或调用MPI_TYPE_VECTOR(1, count, n, oldtype,
newtype), n为升序. </P>
<P>Hvector(异构向量) </P>
<P>函数MPI_TYPE_HVECTOR和MPI_TYPE_VECTOR基本相同,只是stride不再是元素个数,而是字节数.这两种生成器的使用在3.12.7中说明.
H代表异构(Heterogeneous"). </P>
<P><TT>MPI_TYPE_HVECTOR(count,blocklength,stride,oldtype,newtype) </TT></P>
<UL>
<P><TT>IN count 块的数量(非负整数) </TT></P>
<P><TT>IN blocklength 每个块中所含元素个数(非负整数) </TT></P>
<P><TT>IN stride 各块起始位置之间相隔的字节数(整数) </TT></P>
<P><TT>IN oldtype 旧数据类型(句柄) </TT></P>
<P><TT>OUT newtype 新数据类型(句柄) </TT></P>
</UL>
<P><TT>int MPI_Type_hvector(int count,int blocklength,MPI_Aint stride,]
MPI_Datatype oldtype,MPI_Datatype *newtype) </TT></P>
<P><TT>MPI_TYPE_HVECTOR(COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,IERROR)
</TT></P>
<UL>
<P><TT>INTEGER COUNT,BLOCKLENGTH,STRIDE,OLDTYPE,NEWTYPE,IERROR </TT></P>
</UL>
<P>假设oldtype的类型映像为 </P>
<UL>
<P>{(type0, disp0), (typen-1,dispn-1 )}, </P>
</UL>
<P>extent = ex. 设bl为blocklength.新创建的数据类型有count.bl.n个入口项,类型映像为
</P>
<UL>
<P>{(type0, disp0),..., (typen-1,dispn-1 ), </P>
<P>(type0, disp0+ex),..., (typen-1, dispn-1+ex),..., </P>
<P>(type0, disp0+ex.(bl-1)),..., (typen-1, dispn-1+ex.(bl-1)), </P>
<P>(type0, disp0+stride),..., </P>
<P>(typen-1, dispn-1+stride),..., </P>
<P>(type0, disp0+stride+ex.(bl-1)),..., (typen-1, dispn-1+stride+ex.(bl-1)),...,
</P>
<P>(type0, disp0+ stride.(count-1)),..., (typen-1, dispn-1+stride.(count-1)),...,
</P>
<P>(type0, disp0+stride.(count-1)+(bl-1).ex),..., </P>
<P>(typen-1, dispn-1+stride.(count-1)+(bl-1).ex)}. </P>
</UL>
<P>Indexed(索引) </P>
<P>MPI_TYPE_INDEXED允许复制一个旧数据类型到一个块序列中(每个块是旧数据类型的一个连接),每个块可以包含不同的拷贝数目和具有不同的偏移.所有的块偏移都是旧数据类型extent的倍数.
</P>
<P><TT>MPI_TYPE_INDEXED(count,array_of_blocklengths,array_of_displacemets,oldtype,newtype)
</TT></P>
<UL>
<P><TT>IN count 块的数量--同时也是array_of_displacements 和 array_of_blocklengths
中的项数(非负整数) </TT></P>
<P><TT>IN array_of_blocklengths 每个块中所含元素个数(非负整数数组) </TT></P>
<P><TT>IN array_of_displacements 各块偏移值,oldtype的extent的倍数(整数数组)
</TT></P>
<P><TT>IN oldtype 旧数据类型(句柄) </TT></P>
<P><TT>OUT newtypr 新数据类型(句柄) </TT></P>
</UL>
<P><TT>int MPI_Type_indexed(int count,int *array_of_blocklengths, int *array_of_displacements,
MPI_Datatype oldtype, MPI_Datatype *newtype) </TT></P>
<P><TT>MPI_TYPE_INDEXED(COUNT,ARRAY_OF_BLOCKLENGTHS,ARRAY_OF_DISPLACEMENTS,OLDTYPE,NEWTYPE,IERROR)
</TT></P>
<UL>
<P><TT>INTEGER COUNT,ARRAY_OF_BLOCKLENGTHS(*),ARRAY_OF_DISPLACEMENTS(*),
OLDTYPE,NEWTYPE,IERROR </TT></P>
</UL>
<P>例3.22设oldtype的类型映像为{(double,0),(char,8)},extent=16.令B=(3,1),D=(4,0),则MPI_TYPE_INDEXED(2,B,D,oldtype,newtype)调用生成的数据类型映像是:
</P>
<UL>
<P>{(double,64),(char,72), (double,80),(char,88),(double,96),(char,104),
(double,0),(char,8)}. </P>
</UL>
<P>即,旧类型的三个拷贝在偏移64处开始,一个拷贝从0偏移开始. </P>
<P>一般地,假设oldtype的类型映像为 </P>
<UL>
<P>{(type0, disp0), (typen-1,dispn-1 )}, </P>
</UL>
<P>extent = ex. 设B为array_of_blocklengths参数,D为array_of_displacements参数.新创建的数据类型有n.SUM(B[i],i=0,...,count-1)项,类型映像为
</P>
<UL>
<UL>
<P>{(type0, disp0+D[0].ex),..., (typen-1,dispn-1 +D[0].ex),..., </P>
<P>(type0, disp0+(D[0]+B[0]-1).ex),..., (typen-1, dispn-1+(D[0]+B[0]-1).ex),...,</P>
<P>(type0, disp0+D[count-1].ex),..., (typen-1, dispn-1+D[count-1].ex),...,
</P>
<P>(type0, disp0+(D[count-1]+B[count-1]-1).ex),..., </P>
<P>(typen-1, dispn-1+(D[count-1]+B[count-1]-1).ex.)}. </P>
</UL>
</UL>
<P>一个MPI_TYPE_VECTOR(count,blocklength,stride,oldtype,newtype)等价于调用MPI_TYPE_INDEXED(count,B,D,oldtype,newtype),
其中 </P>
<UL>
<UL>
<P>D[j]=j.stride, j=0,...,count-1, </P>
<P>B[j]=blocklength, j=0,...,count-1. </P>
</UL>
</UL>
<P>Hindex(异构索引) </P>
<P>函数MPI_TYPE_HINDEXED和MPI_TYPE_INDEXED基本相同,只是array_of_displacements中的块偏移不再是旧数据类型extent的倍数,而是字节数.
</P>
<P><TT>MPI_TYPE_HINDEXED(count,array_of_blocklengths,array_of_displacemets,oldtype,newtype)
</TT></P>
<UL>
<P><TT>IN count 块的数量--同时也是array_of_displacements 和 array_of_blocklengths
中的项数(整数) </TT></P>
<P><TT>IN array_of_blocklengths 每个块中所含元素个数(非负整数数组) </TT></P>
<P><TT>IN array_of_displacements 各块偏移字节数(整数数组) </TT></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -