第三章 点对点通讯

FORWARD BACKWARD ROOT CONTENT HELP HOME


第三章 点对点通讯


3.1 介绍

由进程完成的消息的发送和接收是基本的MPI 通讯机制。基本的点对点通讯操作是send 和receive 。他们的用法在下面例子中解释。

在这个例子中, 进程0 (myrank = 0)使用发送操作MPI_SEND给进程1 发送消息。这个操作在发送存储器中指定一个缓存,消息数据取自这个缓存。在上例中,这个发送缓存是在进程0 中包含变量message 的存储空间。发送缓存的位置、尺寸和类型由发送操作的前三个参数指定。被发送的消息将包含这个变量的13 个符号。另外,发送操作把消息和一个信封联系起来。这个信封指定消息的目的地,并包含由receive操作为选择一个特定消息而使用的识别信息。发送操作的后三个参数为所发送的消息指定信封。

进程1 用接收操作MPI_RECV接收这个信号。将被接收的消息按着其信封的值被选择, 消息数据被存入接收缓存。在上面的例子中,接收缓存是由进程1 的存储器中包含串message的存储空间。接收操作的前三个参数指定接收缓存的位置、尺寸和类型。之后的三个参数用于选择将来的消息。最后的参数用于返回所接收消息的信息。   

下一节描述阻塞发送和接收操作。我们讨论发送、接收、阻塞通讯语义、类型匹配要求、在异构环境中类型匹配,和更一般的通讯模式。非阻塞通讯在下一部分,然后类通道结构和发送-接收操作。其次,我们考虑一般的数据类型,它我们能有效地转换异构的和非连续的数据。最后,我们以消息的显示打包和拆包调用结束本章。


3.2 阻塞发送和接受操作

3.2.1 阻塞发送

下面给出阻塞发送操作的语法。

MPI_SEND(buf,count,datatype,dest,tag,comm)

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

MPI_SEND(BUF, COUNT, DATATYPE, DEST, TAG, COMM, IERROR)

在3.4节描述这个调用的阻塞语义。


3.2.2 消息数据

MPI_SEND操作指定的发送缓存是由count个类型为datatype的连续数据空间组成, 起始地址为buf。注意我们不是以字节数, 而是以元素的个数指定消息的长度,后者是独立于机器并且更接近应用级。   

消息的数据部分是由count个连续值组成, 由datatype指定其类型。count可以是零, 这种情况下消息的数据部分是空的。为消息的数据值指定的基本数据类型应相应于宿主语言的基本数据类型。下表列出对于Fortran语言这个参数类型的可能值和相应的Fortran语言类型。      


     MPI datatype           Fortran datatype  
          MPI_INTEGER            INTEGER  
          MPI_REAL               REAL 
          MPI_DOUBLE_PRECISION   DOUBLE PRECISION 
          MPI_COMPLEX            COMPLEX
          MPI_LOGICAL            LOGICAL 
          MPI_CHARACTER          CHARACTER(1) 
          MPI_BYTE             
          MPI_PACKED 


下表列出对于C 语言这个参数类型的可能值和相应的C 语言类型。


     MPI datatype            C datatype 
          MPI_CHAR                signed char 
          MPI_SHORT               signed short int 
          MPI_INT                 signed int 
          MPI_LONG                signed long int 
          MPI_UNSIGNED_CHAR       unsigned char 
          MPI_UNSIGNED_SHORT      unsigned short int 
          MPI_UNSIGNED            unsigned int 
          MPI_UNSIGNED_LONG       unsigned long int 
          MPI_FLOAT               float 
          MPI_DOUBLE              double
          MPI_LONG_DOUBLE         long double 
          MPI_BYTE 
          MPI_PACKED 

MPI_BYTE和MPI_PACKED数据类型没有相应于一个Fortran 或C 的数据类型。类型MPI_BYTE的一个值由一个字节组成( 8个二进制位)。一个字节是不用解释的,不同于一个字符。对于字符不同的机器有不同的表示方法,或者可以用一个以上的字节表示字符。另一方面,在所有的机器上,一个字节有相同的二进制值。类型MPI_PACKED将在3.13节解释。   

MPI要求支持上述数据类型, 匹配Fortran 77 和ANSI C的基本数据类型。如果宿主语言有下列附加的数据类型,那么应提供附加的MPI数据类型,

基本原理. 本设计的一个目的是允许MPI作为一个库被实现, 而不要求附加的预处理或编译。所以编程者不能假设一个通讯调用有关于通讯缓存中变量数据类型的信息;这个信息必须由一个显示参数提供。在3.3.2节将清楚这些数据类型信息的要求。(基本原理结束)。


3.2.3 消息信封

除了数据部分, 消息带有用于识别消息并选择接收他们的信息。这个信息是由确定的域数组成的,我们称信封。这些域有 :         

消息的source隐含地由消息发送者的标识来决定。其他域是由发送操作的参数决定。   

消息的destination是由dest参数指定。   

整型值的消息tag是由tag参数指定。这个整型能被程序用于识别不同的消息。有效标识值的范围是0,...,UB, 这儿UB的值依赖于实现。在第七章描述,通过查寻属性MPI_TAG_UB的值, 可得到UB。MPI要求UB不小于32767。 comm参数指定用于发送操作的communicator。在第五章解释communicator; 下面是其使用的简介。   

一个通讯组( communicator )给一个通讯操作指定上下文。每个通讯上下文提供一个单独的“通讯全域”:消息总是在其被发送的上下文内被接收,不同的上下文发送的消息互不干涉。   

通讯组也指定共享这个通讯上下文的进程组。这个进程组被编号,并且进程是由这个组中的进程号标识。所以,dest的有效值范围是0,...,n-1, 其中n是该组中的进程数。( 如果这个进程组是一个通讯组间的, 那么destination是由远程组中他们的进程号所标识。请看第五章)。   

一个预定义的通讯组MPI_COMM_WORLD是由MPI提供。MPI初始化后, 它允许和可存取的全部进程通讯, 进程是由他们在MPI_COMM_WORLD组中的进程号所标识。   


3.2.4 阻塞接收 下面给出阻塞接收的语发。

MPI_RECV(buf,count,datatype,source,tag,comm,status)

int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

MPI_RECV(BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, STATUS, IERROR)

这个调用的阻塞语义在3.4节描述。   

接收缓存由包含count个类型为datatype的连续元素空间组成, 起始地址为buf。被接收消息的长度必须小于或等于接收缓存的长度。如果数据不适合,没有截断,接收缓存发生溢出错误。   

如果一个短于接收缓存的消息到达,那么只有相应于这个消息的那些地址被修改。   

一个接收操作对消息的选择是由消息的信封值管理的。如果消息的信封与接收操作所指定的值source,tag和comm相匹配, 那么这个接收操作能接收这个消息。接收者可以给source指定一个任意值 MPI_ANY_SOURCE ,和/或给tag一个任意值MPI_ANY_TAG, 表明任何source 和/或tag 是可接收的。不能给comm指定任意值。所以, 如果一个消息被寄给接收进程, 有匹配的通讯组, 有匹配的 source ( 如果source = MPI_ANY_SOURCE不成立), 有匹配的tag( 如果tag = MPI_ANY_TAG 不成立 ),那么这个消息能被这个接收操作接收。   

消息的 tag 是由接收操作的 tag 参数指定的。参数 source 如果不同于MPI_ANY_SOURCE, 那么被指定为同一通讯组( 远程进程组, 组间通讯组 )的进程组的一个进程号。所以source参数的有效值范围是0,...,n-1 U MPI_ANY_SOURCE,n是这个进程组中进程的个数。   

注意发送和接收操作间的不对称性:一个接收操作可以接收任何发送者的消息,另一方面,一个发送操作必须指明一个单独的接收者。这和“push”通讯机制匹配,数据转换是受发送者影响( 而不是“pull”机制,数据转换是受接收者影响)。   

Source = destination 是允许的, 即, 一个进程可以给自己发送一个消息。( 但用上述描述的阻塞发送和接收操作是不安全的, 因为这可能导致死锁。看3.5节 。)

3.2.5 返回状态

如果接收操作中使用任意值, 那么可以不知道一个被接收消息的source 或tag。这个信息被MPI_RECV的status(状态)返回。status的类型是MPI定义的。状态变量须由用户显示分配, 即, 他们不是系统对象。   

在C 语言中, status是一个结构, 其包含两个域, 名字为 MPI_SOURCE和MPI_TAG, 该结构可以包含附加域。所以,status.MPI_SOURCE 和status.MPI_TAG分别包含被接收消息的源和目的。   

Fortran中,status是一个MPI_STATUS_SIZE大小的INTEGER型数组。两个常量MPI_SOURCE和MPI_TAG是存储源和标识域的下标号。所以,status(MPI_SOURCE) 和status(MPI_TAG)分别包含被接收消息的源和标识。

这个状态参数也返回关于被接收消息的长度信息。但是,这个信息不能作为状态变量的一个域被直接得到,要求对MPI_GET_COUNT的调用“解码”这个信息。

MPI_GET_COUNT(status,datatype,count)

IN status 接收操作的返回状态

(Status) IN datatype 每个接收缓存元素的数据类型

(handle) OUT count 被接收元素的个数(integer)

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

MPI_GET_COUNT(STATUS, DATATYPE, COUNT, IERROR)

INTEGER STATUS(MPI_STATUS_SIZE), DATATYPE, COUNT, IERROR

返回被接收元素的个数。( 再一次注意,我们以元素而不是字节来计数)这个datatype 参数应与设置 status 变量的接收调用提供的参数匹配。( 我们在后面3.12.5节可以看到, 在某些情况下, MPI_GET_COUNT可以返回值MPI_UNDEFINED。)

所有的发送和接收操作都以这节描述的阻塞MPI_SEND和MPI_RECV操作同样的方式使用buf, count, datatype, source, dest, tag, comm和status参数。


3.3 数据类型匹配和数据转换

3.3.1 类型匹配规则

我们能认为消息传送由下列三个阶段组成。   

在这三个阶段,没有看到类型匹配:在发送者缓存中的每个变量类型必须匹配该发送操作为输如指定的类型;由发送操作指定的类型必须匹配由接收操作指定的类型;在接收缓存中的每个变量的类型必须匹配由接收操作为指定的类型。不能观察到这三个规则的程序是错误的。   为了更精确地定义,我们必须处理两个问题:宿主语言的类型和通讯操作指定的类型匹配;发送者和接收者的类型匹配。   

如果发送和接收操作使用同一类型名字,那么这个发送和接收操作类型匹配(第二阶段)。即,MPI_INTEGER匹配MPI_INTEGER, MPI_REAL匹配MPI_REAL, 等等。对这个规则, 有一个例外, 将在3.13节中讨论, 类型MPI_PACKED能匹配任何其它类型。   

如果由通讯操作所使用的数据类型名字相应于宿主程序变量的基本类型,那么宿主程序中该变量的类型匹配通讯操作所指定的类型。例如,类型名为MPI_INTEGER的输入匹配Fortran的变量类型INTEGER。在3.2.2节给出Fortran和C的这种对照表。对最后这个规则,有两个例外:类型为名为MPI_BYTE或MPI_PACKED的一个输入能用于匹配任何存储字节(在一个字节地址的机器上), 不论包含这个字节的变量类型是什么。类型MPI_PACKED用于发送己显式打包的数据, 或接收将被显式拆包的数据,看3.13节。类型MPI_BYTE允许我们传送不变的存储中的一个字节的二进制值。   

总之,类型匹配规则分为下面三个部分。   .

类型值(例如, 不同于MPI_BYTE)的通讯必须都匹配, 发送调用中的、发送者程序中的, 接收调用中和接收者程序中相应输入的数据类型匹配。

无类型值的通讯(例如, MPI_BYTE数据类型), 发送者和接收者使用MPI_BYTE类型。在这种情况下,对发送者和接收者程序的相应输入类型无要求,也不要求他们类型一样。   .

关于打包数据的通讯, 使用MPI_PACKED。    

下面的例子解释了前两种情况。

例子3.1 发送者和接收者指定匹配的类型。

CALL MPI_COMM_RANK(comm, rank, ierr)

FORWARD BACKWARD ROOT INDEX HELP HOME

Copyright: NPAC, PACT
Lastmodified: 1996.11.4th