processor name中,这和FORTRAN77冲的没有区别:整型变量myid和numprocs分别用来记 录某一个并行执行进程的标识和所有参加计算的进程的个数:namelen是实际得到的机器名 字的长度。 第三部分,MPI程序的开始和结束必须是MPI_Init和MPI_Finalize, 分别完成MPI程序的 初始化和结束工作。对比FORTRAN77+MPI程序,这两个调用在FORTRAN77和C中所需要 的参数是不同的,而且习惯上,在FORTRAN77中,所有的MPI调用均为大写(由于FORTRAN77 源程序对大小写无关,因此使用小写的程序仍然是正确的,这里全用大写主要是遵守MPI书 写FORTRAN7程序的惯例),而在C中则是以“MPI”开头,后面的部分第一个字母大写 而其它的后续部分小写。 第四部分,MPI程序的程序体,包括各种MPI过程调用语句和C语句。MPI_Comm_rank 得到当前正在运行的进程的标识号,放在myid中;MPI_Comm_size得到所有参加运算的进 程的个数,放在numprocs中;MPI Get processor name得到本进程运行的机器的名称,结果 放在rocessor name中,它是一个字符串,而该字符串的长度放在namelen中:forintf语句将 本进程的标识号,并行执行的进程的个数,本进程所运行的机器的名字打印出来 和一般的 C程序不同的是这些程序体中的执行语句是并行执行的,每 进程都要执行, 不妨指定才 程序启动时共产生4个进程同时运行,而运行本程序的机器的机器名为“5”4个进程都在 p5上运行,其标识分别为0,1,2,3,执行结果如图11所示,虽然这一MPI程序本身只有 一条打印语句,但是由于它启动了四个进程同时执行,每个进程都执行打印操作,故而最终 的执行结果有四条打印语句。本程序的执行流程和上面的FORTRAN77+MPI的实现版本是 样的。 Hello World!Processof4 on tp5 Hello World!Process 1 of 4 on tp5 Hello World!Process 3 of4 on tp5 Hello world!Process 2 of 4 on to5 图11第一个C+MP程序在1台机器上的执行结果 如果该程序在4台不同的机器p1,p3,tp4,p5上执行,则其最终的执行结果将如图12 所示,即4个进程所运行的机器是不同的。由于4个进程同时执行,在本程序中没有限定打印 语句的顺序,因此不管哪个进程的打印语句在前,哪个在后,都没有关系,只要有4条正确 的输出语句即可。对比FORTR AN77+MPI C+MPI程序的输出结果不难发现不管是在一台 机器上运行,还是在多台机器上运行,其最终执行输出结果是完全一样的。 Hello World!Process 0 of 4 on tp5 Hello World Process1 of4 onp Hello World!Process3of4 on tp 图12第一个C+MP程序在4台机器上的执行结果 19
19 processor_name中 这和FORTRAN77中的没有区别 整型变量myid和numprocs分别用来记 录某一个并行执行进程的标识和所有参加计算的进程的个数 namelen是实际得到的机器名 字的长度 第三部分 MPI程序的开始和结束必须是MPI_Init和MPI_Finalize 分别完成MPI程序的 初始化和结束工作 对比FORTRAN77+MPI程序 这两个调用在FORTRAN77和C中所需要 的参数是不同的 而且习惯上 在FORTRAN77中 所有的MPI调用均为大写 由于FORTRAN77 源程序对大小写无关 因此使用小写的程序仍然是正确的 这里全用大写主要是遵守MPI书 写FORTRAN77程序的惯例 而在C中则是以 MPI_ 开头 后面的部分第一个字母大写 而其它的后续部分小写 第四部分 MPI程序的程序体 包括各种MPI过程调用语句和C语句 MPI_Comm_rank 得到当前正在运行的进程的标识号 放在myid中 MPI_Comm_size得到所有参加运算的进 程的个数 放在numprocs中 MPI_Get_processor_name得到本进程运行的机器的名称 结果 放在processor_name中 它是一个字符串 而该字符串的长度放在namelen中 fprintf语句将 本进程的标识号 并行执行的进程的个数 本进程所运行的机器的名字打印出来 和一般的 C程序不同的是这些程序体中的执行语句是并行执行的 每一个进程都要执行 不妨指定本 程序启动时共产生4个进程同时运行 而运行本程序的机器的机器名为 tp5 4个进程都在 tp5上运行 其标识分别为0 1 2 3 执行结果如图 11所示 虽然这一MPI程序本身只有 一条打印语句 但是由于它启动了四个进程同时执行 每个进程都执行打印操作 故而最终 的执行结果有四条打印语句 本程序的执行流程和上面的FORTRAN77+MPI的实现版本是一 样的 图 11 第一个C+MPI程序在1台机器上的执行结果 如果该程序在4台不同的机器tp1 tp3 tp4 tp5上执行 则其最终的执行结果将如图 12 所示 即4个进程所运行的机器是不同的 由于4个进程同时执行 在本程序中没有限定打印 语句的顺序 因此不管哪个进程的打印语句在前 哪个在后 都没有关系 只要有4条正确 的输出语句即可 对比FORTRAN77+MPI和C+MPI程序的输出结果 不难发现不管是在一台 机器上运行 还是在多台机器上运行 其最终执行输出结果是完全一样的 图 12 第一个C+MPI程序在4台机器上的执行结果 Hello World! Process 0 of 4 on tp5 Hello World! Process 1 of 4 on tp5 Hello World! Process 3 of 4 on tp5 Hello World! Process 2 of 4 on tp5 Hello World! Process 0 of 4 on tp5 Hello World! Process 1 of 4 on tp1 Hello World! Process 2 of 4 on tp3 Hello World! Process 3 of 4 on tp4
#include"mpi.h" #include <stdio h> #include smath h> oid main(argc,argv) int argc; char *argv[] int char processor_name[MPI_MAX_PROCESSOR NAME] MPI_Init(&argc,&argv): MPI Comm rank(MPI COMM WORLD.&myid): MPl_Comm_size(MPI_COMM_WORLD.&nu cs) MPI_Get_processor_name(processor_name.&namelen), fprintf(stderr,"Hello World!Process%d of%d on%n". myid,numprocs,processor name). MPI_FinalizcO). 程序3第一个C+MP饼行程序 program main use mpi character(MPI MAX PROCESSOR NAME)processor name integer myid,numprocs,namelen,r.iem call MPI_INIT(ierr call MPI COMM RANK(MPI COMM WORLD.mvid.iemr call MPI COMM SIZE(MPI COMM WORLD,numprocs,ierr call mPI GET PROCESSOR NAME(processor name namelen ierr) World!Process"myid,"of"numprocs,"onprocessor name call MPI_FINALIZE(re) end 程序4第-个Fortran90+MPI程序 20
20 程序 3 第一个C+MPI并行程序 程序 4 第一个Fortran90+MPI 程序 #include "mpi.h" #include <stdio.h> #include <math.h> void main(argc,argv) int argc; char *argv[]; { int myid, numprocs; int namelen; char processor_name[MPI_MAX_PROCESSOR_NAME]; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Get_processor_name(processor_name,&namelen); fprintf(stderr,"Hello World! Process %d of %d on %s\n", myid, numprocs, processor_name); MPI_Finalize(); } program main use mpi character * (MPI_MAX_PROCESSOR_NAME) processor_name integer myid, numprocs, namelen, rc, ierr call MPI_INIT( ierr ) call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr ) call MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ierr ) call MPI_GET_PROCESSOR_NAME(processor_name, namelen, ierr) print *,"Hello World! Process ",myid," of ", numprocs, " on", processor_name call MPI_FINALIZE(rc) end
从上面的简单例子可以看出,一个MP程序的框架结构可以用图13表示。把握了其结 构之后,下面的主要任务就是掌握MPI提供的各种通信方法与手段。 头文件 相关变量声明 程序开始 程序体 计算与通信 程序结束 图13MP程序的框架结构 5.2MPI程序的一些惯例 下面在上述例子的基础上,介绍一下MPI的命名规则等通常的惯例。 所有MPI的名字都有前缀“MPI”,不管是常量、变量还是过程或函数调用的名字都是 这样。在自己编写的程序中不准说明以前缀“MPL”开始的任何变量和函数,这样做的主 要目的是为了避免与MPI可能的名字混淆。 FORTRAN形式的MPI调用,一毅全为大写(虽然FORTRAN不区分大小写),而C形式 的MPI调用,则为MPI_Aaaa_aaa的形式。 所有MPI的FORTRAN-子程序在最后参数中都有一个返回代码,对于成功的返回代码值 是MPI_SUCCESS,其他的错误代码是依赖于实现的。一些MPI操作是函数,它没有返回代 码参数。 FORTRAN中的句柄以整型表示,二值变量是逻组类型。FORTRAN的数组下标是以1开 始,但在C中是以0开始。 除非明显说明,FORTRAN T7的MP程序与ANSI FORTRAN7标准标准相一致。但有些 地方不同于ANSI FORTRAN刀标准,比如: >MPI标识符限于三十个有效符号,而不是六个。 >MPI标识符可在第一个字符后包含下划线。 >具有一个选择参数的MP子程序可以用不同的参数类型来调用。 之
21 从上面的简单例子可以看出 一个MPI程序的框架结构可以用图 13表示 把握了其结 构之后 下面的主要任务就是掌握MPI提供的各种通信方法与手段 头文件 相关变量声明 程序开始 程序体 计算与通信 程序结束 图 13 MPI程序的框架结构 5.2 MPI程序的一些惯例 下面在上述例子的基础上 介绍一下MPI的命名规则等通常的惯例 所有MPI的名字都有前缀 MPI_ 不管是常量 变量还是过程或函数调用的名字都是 这样 在自己编写的程序中不准说明以前缀 MPI_ 开始的任何变量和函数 这样做的主 要目的是为了避免与MPI可能的名字混淆 FORTRAN形式的MPI调用 一般全为大写 虽然FORTRAN不区分大小写 而C形式 的MPI调用 则为MPI_Aaaa_aaa的形式 所有MPI的FORTRAN子程序在最后参数中都有一个返回代码 对于成功的返回代码值 是MPI_SUCCESS 其他的错误代码是依赖于实现的 一些MPI操作是函数 它没有返回代 码参数 FORTRAN中的句柄以整型表示 二值变量是逻辑类型 FORTRAN的数组下标是以1开 始 但在C中是以0开始 除非明显说明 FORTRAN 77的MPI程序与ANSI FORTRAN 77标准标准相一致 但有些 地方不同于 ANSI FORTRAN 77 标准 比如 ÿ MPI标识符限于三十个有效符号, 而不是六个 ÿ MPI标识符可在第一个字符后包含下划线 ÿ 具有一个选择参数的MPI子程序可以用不同的参数类型来调用 ÿ 在一个包含文件mpif.h中提供所命名的常量 ÿ 在支持用户定义类型的系统中, 鼓励生产商在mpif.h文件中提供类型说明
5.3小结 从上面的例子,我们对MPI程序已经有了一定的感性认识,由于MPI并行程序是在原来 串行程序基础上的扩展,在许多地方和串行程序是相同的,串行程序设计的许多经验是可以 应用到并行程序设计中的,但是同时我们必须注意,在设计MP程序的时候,头脑中必须有 程序并行执行的概念,而不是原来有序的串行执行,这才是串行和并行最主要的区别。 本章的主要目的是通过简单的例子,给出MP程序的框架结构,使读者对MPI程序有 个简单的总体上的认识,为出,采用了最常见也是最简单的Hel0W0d”程序,它虽姚不 不涉及任何通信的成分,但是,它给出了MP程序与原来的串性程序的主要区别,它可以同 时打印出多个“Hello World”语句,而不是一个,它已经包含了SPMD(Single Program Multiple Data)程序的精随
22 5.3 小结 从上面的例子 我们对MPI程序已经有了一定的感性认识 由于MPI并行程序是在原来 串行程序基础上的扩展 在许多地方和串行程序是相同的 串行程序设计的许多经验是可以 应用到并行程序设计中的 但是同时我们必须注意 在设计MPI程序的时候 头脑中必须有 程序并行执行的概念 而不是原来有序的串行执行 这才是串行和并行最主要的区别 本章的主要目的是通过简单的例子 给出MPI程序的框架结构 使读者对MPI程序有一 个简单的总体上的认识 为此 采用了最常见也是最简单的 Hello World 程序 它虽然还 不涉及任何通信的成分 但是 它给出了MPI程序与原来的串性程序的主要区别 它可以同 时打印出多个 Hello World 语句 而不是一个 它已经包含了SPMD Single Program Multiple Data 程序的精髓
第6章六个接口构成的MPI子集 在MPL-1中,共有128个调用接口,在MPI-2中有287个,应该说MPI是比较庞大的,完全 掌握这么多的调用对于初学者来说是比较困难的。但是,从理论上说,MP所有的通信功能 可以用它的6个基本的调用来实现,掌握了这6个调用,就可以实现所有的消息传递并行程序 的功能,因此, 首先在本章介绍这6个最基本的调用。 本章首先对这6个调用组成的完备子集进行介绍,然后用例子说明如何用这6个调用来完 成基本的消息传递并行编程最后介绍了MPI数据类型和MPI消息。 6.1子集介绍 这一部分对6个调用构成的MP完备子集进行介绍,在对具体的调用介绍之前,首先介 绍MPI是如何对其FORTRAN 77和C的调用进行说明的。 6.1.1MPI调用的参数说明 独立于具体语言的说明 C形式的原型说明 FORTRAN形式的原型说明 图14MPI调用的说明格式 如图14所示,对于有参数的MP调用,MPI首先给出一种独立于具体语言的说明,对名 个参数的性质进行介绍,然后在给出它相对于FORTRAN77和C的原型说明,在MPL2中还 给出了C+形式的说明。MP附参数说明的方式有三种,分别是N、OUT和NOUT。它们的 含义分别是: N(输入):调用部分传递给MPI的参数,MPI除了使用该参数外不允许对这一参 数做任何修改 ·OUT(输出):MP返回给调用部分的结果参数,该参数的初始值对MP没有任何 意义 NOUT(输入输出):调用部分首先将该参数传递给MPL,MPI对这一参数引用 修改后,将结果返回给外部调用,该参数的初始值和返回结果都有意义 如果某一个参数在调用前后没有改变,比如某个隐含对象的句柄,但是该句柄指向的对 象被修改了,这一参数仍然被说明为OUT或NOUT。MPI的定义在最大范围内避免NOUT 参数的使用,因为这些使用易于出错,特别是对标量参数。 23
23 第6章 六个接口构成的MPI子集 在MPI-1中 共有128个调用接口 在MPI-2中有287个 应该说MPI是比较庞大的 完全 掌握这么多的调用对于初学者来说是比较困难的 但是 从理论上说 MPI所有的通信功能 可以用它的6个基本的调用来实现 掌握了这6个调用 就可以实现所有的消息传递并行程序 的功能 因此 首先在本章介绍这6个最基本的调用 本章首先对这6个调用组成的完备子集进行介绍 然后用例子说明如何用这6个调用来完 成基本的消息传递并行编程 最后介绍了MPI数据类型和MPI消息 6.1 子集介绍 这一部分对6个调用构成的MPI完备子集进行介绍 在对具体的调用介绍之前 首先介 绍MPI是如何对其FORTRAN 77和C的调用进行说明的 6.1.1 MPI调用的参数说明 独立于具体语言的说明 C形式的原型说明 FORTRAN形式的原型说明 图 14 MPI调用的说明格式 如图 14所示 对于有参数的MPI调用 MPI首先给出一种独立于具体语言的说明 对各 个参数的性质进行介绍 然后在给出它相对于FORTRAN 77和C的原型说明 在MPI-2中还 给出了C++形式的说明 MPI对参数说明的方式有三种 分别是IN OUT和INOUT 它们的 含义分别是 l IN 输入 调用部分传递给MPI的参数 MPI除了使用该参数外不允许对这一参 数做任何修改 l OUT 输出 MPI返回给调用部分的结果参数 该参数的初始值对MPI没有任何 意义 l INOUT 输入输出 调用部分首先将该参数传递给MPI MPI对这一参数引用 修改后 将结果返回给外部调用 该参数的初始值和返回结果都有意义 如果某一个参数在调用前后没有改变 比如某个隐含对象的句柄 但是该句柄指向的对 象被修改了 这一参数仍然被说明为OUT或INOUT MPI的定义在最大范围内避免INOUT 参数的使用,因为这些使用易于出错 特别是对标量参数