📄 mpi312.htm
字号:
<P><A NAME="3.12.5"></A></P>
<P>3.12.5 使用通用数据类型通信 </P>
<P>指向派生数据类型的句柄可以在任何要求数据类型参数的地方传递给一个通信调用.
类似MPI_SEND(buf, count, datatype,...)的调用, 当count>1时被解释成如同这个调用被传给了一个新的数据类型,
这个新的数据类型是coun个datatype拷贝的连接. 于是, MPI_SEND ( buf, count,
datatype, dest, tag, comm )等价于: </P>
<P><TT>MPI_TYPE_CONTIGUOUS( count, datatype, newtype ) </TT></P>
<P><TT>MPI_TYPE_COMMIT( newtype) </TT></P>
<P><TT>MPI_SEND( buf, 1, newtype, dest, tag, comm ). </TT></P>
<P>其它类似的含有count 和datatype参数的通信函数也适用于上面的描述. </P>
<P>假设一个发送操作MPI_SEND( buf, count, datatype, dest, tag, comm )被执行,
其中datatype的数据类型映像为: </P>
<UL>
<P>{(type0, disp0),..., (typen-1,dispn-1 )},</P>
</UL>
<P>并且extent=extent. ("pseudo-type"的MPI_UB 和 MPI_LB因为是空,
所以没有在上面列出,但是他们影响extent的值.) 发送操作发送n.count个项, 其中i.n+j
项在 addri,j = buf + extent.i + dispj , 且当 I = 0,..., count-1, j=0,...,n-1时类型为typej.
这些项不需要连续或分布, 他们的顺序可以指定. </P>
<P>在addri,j存放的变量类型应该和typej匹配(见3.3.1). \发送的消息包含n.count个项,其中i.n+j项的类型为typej.
</P>
<P>类似地, 假设接收操作MPI_RECV( buf, count, datatype, source, tag, comm,
status )被执行, 其中datatype的数据类型映像为: {(type0, disp0),..., (typen-1,dispn-1
)}, 并且extent=extent. ("pseudo-type"的MPI_UB 和 MPI_LB因为是空,
所以没有在上面列出,但是他们影响extent的值.) 接收操作接收n.count个项, 其中i.n+j
项在 buf + extent.i + dispj , 且类型为typej. 如果输入的消息含有k个元素,
则k<=n.count; 第i.n+j个元素的类型应该匹配typej. </P>
<P>类型匹配是根据相应数据类型的类型签名来定义的, 也就是基本数据类型部分的序列.
类型匹配并不依赖于数据类型定义的某些方面(如偏移或介质类型). </P>
<P>例 3.27 该例说明了类型匹配是根据一个派生数据类型含有的基本数据类型来定义的.
</P>
<UL>
<P><TT>... </TT></P>
<P><TT>CALL MPI_TYPE_CONTIGUOUS(2, MPI_REAL, type2, ...) </TT></P>
<P><TT>CALL MPI_TYPE_CONTIGUOUS(4, MPI_REAL, type4, ...) </TT></P>
<P><TT>CALL MPI_TYPE_CONTIGUOUS(2, type2, type22, ...) </TT></P>
<P><TT>... </TT></P>
<P><TT>CALL MPI_SEND(a, 4, MPI_REAL, ...) </TT></P>
<P><TT>CALL MPI_SEND(a, 2, type2, ...) </TT></P>
<P><TT>CALL MPI_SEND(a, 1, type22, ...) </TT></P>
<P><TT>CALL MPI_SEND(a, 1, type4, ...) </TT></P>
<P><TT>... </TT></P>
<P><TT>CALL MPI_RECV(a, 4, MPI_REAL, ...) </TT></P>
<P><TT>CALL MPI_RECV(a, 2, type2, ...) </TT></P>
<P><TT>CALL MPI_RECV(a, 1, type22, ...) </TT></P>
<P><TT>CALL MPI_RECV(a, 1, type4, ...) </TT></P>
</UL>
<P>每个发送都和任何一个接收相匹配. </P>
<P>一个数据类型可以指定覆盖项. 如果这样的数据类型在接收中使用的话, 即\接收操作对部分接收缓冲区执行多次写操作,
则调用出错. </P>
<P>假设MPI_RECV( buf, count, datatype, dest, tag, comm, status ) 被执行,
其中datatype的数据类型映像为: </P>
<UL>
<P>{(type0, disp0),..., (typen-1,dispn-1 )}, </P>
</UL>
<P>接收操作不需要填写全部的接收缓冲区,也不需要填写n的倍数的位置.任意k个基本元素都可以接收,
其中0<=k<=count.n. 接收到的基本元素个数可以用查询函数MPI_GET_ELEMENTS从status中获得
</P>
<P><TT>MPI_GET_ELEMENTS(status, datatype, count ) </TT></P>
<UL>
<P><TT>IN status 返回接收操作的状态(Status) </TT></P>
<P><TT>IN datatype 接收操作使用的数据类型(句柄) </TT></P>
<P><TT>OUT count 接收到的基本元素个数(整型) </TT></P>
</UL>
<P><TT>int MPI_Get_elements( MPI_Status status, MPI_Datatype datatype,
int *count) </TT></P>
<P><TT>MPI_GET_ELEMENTS(STATUS,DATATYPE,COUNT,IERROR) </TT></P>
<UL>
<P><TT>INTEGER STATUS(MPI_STATUS_SIZE),DATATYPE,COUNT,IERROR </TT></P>
</UL>
<P>前面定义的函数MPI_CET_COUNT(3.2.5)功能不同.它返回接收到的"最高层元素"个数.
在前文的例子中MPI_GET_COUNT可能返回一个整数k, 0<=k<=count. 如果MPI_GET_COUNT返回k,
则接收到的基本元素个数(MPI_GET_ELEMENTS的返回值)是n.k.如果接收到的基本元素个数不是n的倍数,
即接收操作接收到的不是整数个数据类型的拷贝,则MPI_GET_COUNT返回值为MPI_UNDEFINED.
</P>
<P><TT>例 3.28 MPI_GET_COUNT和MPI_GET_ELEMENT的使用 </TT></P>
<UL>
<P><TT>.... </TT></P>
<P><TT>CALL MPI_TYPE_CONTIGUOUS(2, MPI_REAL, Type2, ierr) </TT></P>
<P><TT>CALL MPI_TYPE_COMMIT(Type2,ierr) </TT></P>
<P><TT>... </TT></P>
<P><TT>CALL MPI_COMM_RANK(comm, rank, ierr) </TT></P>
<P><TT>IF(rank.EQ.0) THEN </TT></P>
<UL>
<P><TT>CALL MPI_SEND(a, 2, MPI_REAL, 1, 0, comm, ierr) </TT></P>
<P><TT>CALL MPI_SEND(a, 3, MPI_REAL, 1, 0, comm, ierr) </TT></P>
</UL>
<P><TT>ELSE </TT></P>
<UL>
<P><TT>CALL MPI_RECV(a, 2, Type2, 0, 0, comm, stat, ierr) </TT></P>
<P><TT>CALL MPI_GET_COUNT(stat, Type2, i, ierr) ! returns i=1 </TT></P>
<P><TT>CALL MPI_GET_ELEMENTS(stat, Type2, i, ierr) ! returns i=2 </TT></P>
<P><TT>CALL MPI_RECV(a, 2, Type2, 0, 0, comm, stat, ierr) </TT></P>
<P><TT>CALL MPI_GET_COUNT(stat, Type2, i, ierr) ! returns i=MPI_UNDEFINED
</TT></P>
<P><TT>CALL MPI_GET_ELEMENTS(stat, Type2, i, ierr) ! returns i=3 </TT></P>
</UL>
<P><TT>END IF </TT></P>
</UL>
<P>函数MPI_GET_ELEMENTS也可以在执行检查操作来检查元素个数之后再调用. 注意MPI_GET_COUNT和MPI_GET_ELEMENT对基本数据类型使用时返回值相同.
</P>
<P>基本原理 </P>
<P>对MPI_GET_COUNT的扩充定义看上去是很自然的: 用户在填写接收缓冲区后可能希望返回count参数的值.
有时datatype代表用户想要传送的一个基本单元数据, 例如, 一个记录矩阵中的一条记录(结构).
用户将可以知道接收到了多少个部分而不必通过拿每个部分所含元素的个数去除接收到的总数来计算.
可是在其它情况下datatye用于定义一个复杂的接收区间的数据分布情况, 而不代表传送的基本数据单元.
在这种情况下用户需要使用MPI_GET_ELEMENTS. </P>
<P>##对执行者的建议## </P>
<P>从定义中可以推出, 一个接收不能改变定义的入口项以外的存储空间的值来填写通信缓冲区.
另外, 一个结构的填充区在该结构从一个进程拷贝到另一个进程时不能被改变. 因为这将阻止把拷贝结构连同填充区优化为一个连续区间.
当不影响计算输出的时候是随时可以优化的. 用户可以通过显式包含填充区作为消息的一部分来实现"强行"优化.
</P>
<P><A NAME="3.12.6"></A></P>
<P>3.12.6 正确使用地址 </P>
<P>在C或FORTRAN中相继定义的变量不一定存储在相连的地方. 因此需要注意对一个变量的偏移不一定对另一个变量适用.
另外, 对于使用段地址空间的机器, 地址是不统一的并且地址计算有各自的特点.
所以address(相对于MPI_BOTTOM开始地址的偏移量)的使用是有限制的 </P>
<P>如果几个变量属于同一个矩阵则属于同一个顺序存储区, 在FORTRAN中属于同一个COMMON块,
在C 中属于同一个结构. 有效的地址定义如下: </P>
<P>1. 函数MPI_ADDRESS返回一个有效地址, 当作为调用程序的一个参数变量传递时.
</P>
<P>2. 通信函数的buf参数等价于一个有效地址, 当作为调用程序的一个参数变量传递时
</P>
<P>3. 如果v是一个有效地址, i是一个整数, 则v+i是一个有效地址, 假设v 和v+i在同一个连续存储区内.
</P>
<P>4. 如果v是一个有效地址, 则MPI_BOTTOM+v是一个有效地址. </P>
<P>一个正确的程序只使用有效地址来标识通信缓冲区中入口项的位置. 而且如果u和v是两个有效地址,
则整数u-v只有当u和v在同一个连续存储区间是才可以被计算. 对于地址, 没有其它的数学运算是有效的.
</P>
<P>上面的规则对于派生数据类型没有限制, 只要他们是用于定义的通信缓冲区完全包含在一个连续存储区内即可.
不过, 构造一个含有不在一个连续存储区的变量的通信缓冲区必须遵循某些限制.
基本地, 一个包含不在同一个连续存储区间的变量的通信缓冲区,只有通过在通信调用中说明buf=MPI_BOTTOM,
count=1, 并且在所有偏移地址是有效绝对地址的地方使用datatype参数的情况下才可以使用.
</P>
<P>##对用户的建议## </P>
<P>MPI执行器并不保证检测偏移量"越界"的错误, --除非覆盖了用户地址空间--因为MPI调用可能不知道本地程序中矩阵和记录的扩充(extent).
</P>
<P>##对执行者的建议## </P>
<P>在一个具有连续地址空间的机器上是不需要用地址(绝对)和偏移(相对)的不同的:
MPI_BOTTOM是零, 地址和篇移量都是整数. 在需要区分的机器上, 地址是靠包含MPI_BOTTOM的表达式来识别的.
</P>
<P><A NAME="3.12.7"></A></P>
<P>3.12.7 一些例程 </P>
<P>下面的例子说明了派生数据类型的使用. </P>
<P>例3.29 发送和接收一个部分三维数组。 </P>
<UL>
<P><TT>REAL a(100,100,100),e(9,9,9) </TT></P>
<P><TT>INTEGER oneslice,twoslice,threeslice,sizeofreal,myrank,ierr </TT></P>
<P><TT>INTEGER status(MPI_STATUS_SIZE) </TT></P>
</UL>
<P><TT>C 提出a(1:17:2, 3:11, 2:10) 并存入e(:,:,:). </TT></P>
<UL>
<P><TT>CALL MPI_COMM_RANK(MPI_COMM_WORLD,myrank) </TT></P>
<P><TT>CALL MPI_TYPE_EXTENT(MPI_REAL,sizeofreal,ierr) </TT></P>
</UL>
<P><TT>C 创建一维数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_VECTOR(9,1,2, MPI_REAL,oneslice,lerr) </TT></P>
</UL>
<P><TT>C 创建二维数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_HVECTOR(9,1,100* sizeofreal,,oneslice,twoslice,lerr)
</TT></P>
</UL>
<P><TT>C 创建整个数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_COMMIT(threeslice,ierr) </TT></P>
<P><TT>CALL MPI_SENDRECV(a(1,3,2),1,threeslice,myrank,0,e,9*9*9,</TT></P>
<UL>
<P><TT>MPI_REAL,myrank,0,MPI_COMM_WORLD,status,ierr) </TT></P>
</UL>
</UL>
<P>例3.30 拷贝矩阵的严格下三角阵 </P>
<UL>
<P><TT>REAL a(100,100),b(100,100) </TT></P>
<P><TT>INTEGER disp(100),blocklen(100),ltype,myrank,ierr </TT></P>
<P><TT>INTEGER status(MPI_STATUS_SIZE) </TT></P>
</UL>
<P><TT>C 拷贝数组a的下三角部分到数组b的下三角部分 </TT></P>
<UL>
<P><TT>CALL MPI_COMM_RANK(MPI_COMM_WORLD,myrank) </TT></P>
</UL>
<P><TT>C 计算每列开始和大小 </TT></P>
<UL>
<P><TT>DO i=1, 100 </TT></P>
<UL>
<P><TT>disp(i) = 100*(i-1) + i </TT></P>
<P><TT>block(i) = 100-i </TT></P>
</UL>
<P><TT>END DO </TT></P>
<P><TT>CALL MPI_TYPE_INDEXED(100,block,disp,MPI_REAL,1type,ierr </TT></P>
<P><TT>CALL MPI_TYPE_COMMTT(1type,ierr) </TT></P>
<P><TT>CALL MPE_SENDRECV(a,1,1type,myrank,0,b,1 1type,myrank,0, MPI_COMM_WORLD,statusierr)
</TT></P>
</UL>
<P>例3.31 转置矩阵 </P>
<UL>
<P><TT>REAL a(100,100),b(100,100) </TT></P>
<P><TT>INTEGER row,xpose,sizeofreal,myrank,ierr </TT></P>
<P><TT>INTEGER status(MPI_STATUS_SIZE) </TT></P>
</UL>
<P><TT>C 转置矩阵a到b </TT></P>
<UL>
<P><TT>CALL MPI_COMM_RANK(MPI_COMM_WORLD,myrank) </TT></P>
<P><TT>CALL MPI_TYPE_EXTENT(MPI_REAL,sizeofreal,ierr) </TT></P>
</UL>
<P><TT>C 为一行创建数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_VECTOR(100,1,100,MPI_REAL,row,ierr) </TT></P>
</UL>
<P><TT>C 以行主元顺序创建矩阵数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_HVECTOR(100,1,sizeofreal,row,xpose,lerr) </TT></P>
<P><TT>CALL MPI_TYPE_COMMIT(xpose,ierr) </TT></P>
</UL>
<P><TT>C 以行主元顺序发送矩阵并以列主元顺序接收 </TT></P>
<UL>
<P><TT>CALL MPI_SENDRECV(a,1,xpose,myrank,0,b,100*100,MPI_REAL,MYRANK,0,
_COMM_WORLD,status,ierr) </TT></P>
</UL>
<P>例 3.32 另一个转置问题 </P>
<UL>
<P><TT>REAL a(100,100), b(100,100) </TT></P>
<P><TT>INTEGER disp(2), blocklen(2), type(2), row, row1, sizeofreal INTEGER
myrank,ierr </TT></P>
<P><TT>INTEGER status(MPI_STATUS_SIZE) </TT></P>
<P><TT>CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank) </TT></P>
</UL>
<P><TT>C (转置矩阵a到b) </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_EXTENT(MPI_REAL, sizeofreal, ierr) </TT></P>
</UL>
<P><TT>C 创建一行的数据类型 </TT></P>
<UL>
<P><TT>CALL MPI_TYPE_VECTOR(100, 1, 100, MPI_REAL, row, ierr) </TT></P>
</UL>
<P><TT>C creat datatype for one row, with the extent of one real number
</TT></P>
<UL>
<P><TT>disp(1) = 0 </TT></P>
<P><TT>disp(2) = sizeofreal </TT></P>
<P><TT>type(1) = row </TT></P>
<P><TT>type(2) = MPI_UB </TT></P>
<P><TT>blocklen(1) = 1 </TT></P>
<P><TT>blocklen(2) = 1 </TT></P>
<P><TT>CALL MPI_TYPE_STRUCT(2, blocklen, disp, type, row1, ierr) </TT></P>
<P><TT>CALL MPI_TYPE_COMMIT(row1, ierr) </TT></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -