作者: Jeff Squyres
译者: 张灏 Zhang Hao
译自: MPI: Getting Started; Definitions and Fundamentals
摘要: 什么是MPI,如何用?什么样的人应该使用MPI?怎样从MPI的开源实现中来学习MPI的一些基本的东西。
消息传递接口(Message Passing Interface)
“MPI”这个词经常在和高性能计算相关的讨论中出现。有时候人们是喜爱的,有时候是蔑视的,而跟多的时候这是一个让疑惑的词。这篇文章会为经理和开发者提供一些关于MPI的基本的信息。
什么是MPI?
常常有人要我快速简要的说明一些什么是MPI。我通常都会强调如下几点:
- MPI表示 Message Passing Interface
- MPI是一个由专家、工业界和学术界组成的大型的委员会定义的一套标准
- MPI的设计强烈的受到了几十年来的并行计算的“最佳实践”的影响
- 虽然MPI的实现都遵循“MPI 标准”,但是实际上现在有两个文档(MPI-1 和 MPI-2)
- MPI的实现为并行应用提供消息传递或者相关的服务
- 有很多种MPI标准的的实现
本质: MPI标准定义了一组函数,使应用程序可以将消息从一个MPI进程送到另一个MPI进程。 MPI实际上定义了比消息传递更多的服务--但是,其核心和灵魂还是MPI进程间的消息传递。
上面第3点提到,实际上MPI标准有两个文档--MPI-1和MPI-2。MPI-1是MPI消息服务的核。它提供了MPI进程间消息传递的抽象语义和机制,同时提供了一些对通常并行计算的有帮助的附加特性。MPI-2提供了MPI-1没有提供的功能扩展,如动态进程控制、单边消息传递(one sided message passing)、和并行I/O等。
既然MPI仅仅是一套标准,它就没有和任何特别的计算机硬件和软件邦定。这样,任何平台都可以获得MPI标准的实现。比如,大多数并行计算硬件供应商都会提供和他们的硬件协调得最好的MPI实现。一些ISV(Independent Software Vendors---独立软件供应商)也会专门提供某些平台家族的相关MPI相关实现。也可以从一些研究者那里获得对于很多不同平台的开源和自由的MPI实现;这些实现一些提供了产品级的使用质量,而还有一些,则单单为了研究项目。
MPI实现
如上面所说,大多数并行计算硬件的供应商有和其硬件高度协调的MPI实现。特别的,提供商会提供其高带宽、低延时的消息传递相关硬件的配套软件。多数使用这些硬件的集群都会使用供应商提供的软件和硬件环境以获得最好的性能。而使用通用组件架构起来的集群对于 MPI实现就有了更多的选择。很多集群管理员会安装几套不同的MPI实现--以根据应用本身来选择MPI实现,而获得最好的性能。
两个值得注意的MPI实现是 LAM/MPI(来自 Indiana 大学)和 MPICH(来自Argonne国家实验室)。这两个MPI实现都有完整的MPI-1支持和部分支持MPI-2。这两个实现都可以在网上自由获得,并可以用于很多并行和集群环境。
本系列中的例子都以LAP/MPI为基础,以获得一致的命令语法,运行语义和输出。注意,既然 MPI是一套标准,这里提供的简单程序应该对任何MPI实现都是通用的。而不同的是不同MPI实现中程序启动的方式。
谁使用MPI?
两种主要人群使用MPI:并行应用开发者和终端用户。
并行应用开发者为了实现并行和消息传递会很频繁的调用MPI函数。他们在MPI实现的运行时环境编译、调试、运行他们的应用程序。MPI应用开发者应该对MPI标准定义的API、特定MPI 实现运行环境的语义、命令和功能最有发言权。
终端用户可以运行一个应用的时候可以并不用在意这个应用使用了MPI(或者这个应用是并行的)。这样,终端用户可能对特殊的MPI实现、命令和运行环境熟悉的程度都不一样。
基本MPI定义
MPI的很多方面都是为了描述如何进行“MPI进程”间的交互。MPI进程通常都是独立的进程,又或是线程。MPI标准没有定义执行模型;这样就留给具体的实现去定义并行应用如何运行,以及MPI进程到底用什么实现。被一起启动的MPI进程的集合才是一个MPI应用。
消息(Message)是从一个有定义的缓冲区(buffer)传送到一个有定义的接收缓冲区。有定义的缓冲区是指这个缓冲区是有类型的可,即typed buffer。你可以这样轻松的来想MPI的使用:整理这个整数的数组并且将其内容送入进程X。使用有定义的缓冲区可以使MPI实现有适应不同平台的能力,因为这样可以保留相应的字节序(endian ordering)。比如,从一台 Sun的工作站发送一个值为7的整数,相应的MPI实现就会自动转换数据的字节序,使得接收进程,即便是一个运行在Intel工作站上的进程,可以正确的接收。
MPI应用编程接口(API)是用三种语言定义的。这样,就可能使用这三种语言来使用MPI的功能。实际上,如果需要,还可以在一个应用中混合使用这三种语言(不过,数据结构需要在不同语言间转换)。常见到的情况是基于c的MPI应用需要调用Fortran的MPI数值库。
"Hello, World" MPI代码示例
下面的代码是一个简单的“Hello World”程序,虽然很简单,但是可以看到基本的MPI c API。
1 #include 2 #include 3 4 int main(int argc, char **argv) { 5 int rank, size; 6 7 MPI_Init(&argv, &argv); 8 MPI_Comm_size(MPI_COMM_WORLD, &size); 9 MPI_Comm_rank(MPI_COMM_WORLD, &rank); 10 printf("Hello, world. I am %d of %d./n", rank, size); 11 MPI_Finalize(); 12 return 0; 13 }
最先用到MPI的是第二行的“#include”。这个头文件是MPI标准规定的,每个MPI实现都要提供。这个头问题必须提供用户MPI程序需要的所有必要的原型和外部变量声明。因此这个头文件需要被任何要使用MPI功能的程序包含。
第7行是我们第一个用到的MPI函数: MPI_INIT 。如其名字中暗含的 MPI_INIT 的主要目的是初始化MPI通信层。 MPI_INIT 必须在其他MPI函数调用前调用。
第8和第9行调用 MPI_COMM_SIZE 和 MPI_COMM_RANK,这两个函数都用到了同一个参数 MPI_COMM_WORLD 。 MPI_COMM_WORLD 是一个预定义的MPI通信字(communicator)----一个 MPI进程的有序集合,也是一个唯一的通信上下文。 MPI_COMM_WORLD 在 MPI_INIT 函数执行后被赋予意义。
函数 MPI_COMM_SIZE 和 MPI_COMM_RANK 查询 MPI_COMM_WORLD 以获得应用中MPI进程的总数并为这些MPI进程分别获得一个唯一的编号。获取的值放在变量 size 和 rank 中。这样,在第九行执行完毕后,MPI进程就知道应用一共有多少单独进程(peer process)和自己在 MPI_COMM_WORLD 中的唯一的标识(注意,rank的计数是从0开始,到size-1结束)。
最后, MPI_FINALIZE 执行MPI进程中MPI层的清理和关闭工作。在 MPI_FINALIZE 后面再调用 MPI函数都是不合法的。
编译程序
每个MPI实现都提供了不同的机制来编译MPI程序。一个实现提供“封装”好的编译器,可以加上 -I,-L和-l等参数,而其调用一个底层的编译器(如:cc,gcc,icc等。)对于“封装”好的编译器,可以简单的理解为将参数直接传给了底层的编译器。
LAM/MPI提供mpicc,mpiCC(对于大小写不敏感的文件系统则是mpic++),和mpif77,分别封装了c,c++和fortran。比如,在LAM/MPI中编译"hello world"程序可以用命令:
$ mpicc hello.c -o hello
我们鼓励用户使用封装好的编译器编译所有MPI应用,而不要直接调用底层的编译器。也建议用户检查MPI实现的环境变量。上面的程序的编译命令也适用于MPICH。
用LAM/MPI运行MPI应用
在并行MPI程序可以运行前,必须用命令lamboot启动LAM/MPI的运行环境。为了简单起见,下面的例子通过 rsh/ssh 在传统的Beowulf集群上运行,并设置为不需要交互的输入登录密码,否则运行下面的例子就会有些麻烦。
lamboot 需要有一个参数来指定一个模式文件(或者是主机文件--hostfile)来指定在哪些机器上启动运行时环境。最简单的模式文件是一个每行都有一个机器名(或者IP地址)的文本文件。下面是一个启动模式文件的例子。文件名为 myhosts
node1.example.com node2.example.com node3.example.com node4.example.com
运行lamboot命令,并带上myhosts作为参数。
$ lamboot myhosts
当lamboot成功完成,LAM的运行时环境就在所有的myhosts文件中列出的节点机上运行起来了。 mpirun 命令用来启动MPI程序。其中 C 开关用来告诉LAM按照模式文件中指定的CPU个数,以每CPU一个MPI进程的方式启动MPI。如果CPU个数没有被指定--如例子中那样,则每个节点认为有一个CPU。如下面的命令启动4个MPI进程:
$ mpirun C hello Hello, world. I am 0 of 4. Hello, world. I am 1 of 4. Hello, world. I am 2 of 4. Hello, world. I am 3 of 4.
mpirun 和 lamboot 都支持很多命令行参数。可以通过参考各自的手册来了解更多的细节。
通过MPICH来运行MPI应用
通过MPICH来运行MPI程序和使用LAM/MPI有些不一样。确定你的执行路径和环境变量都设置正确。同样,你需要一个机器节点文件(这个文件和LAM的模式文件很像)看起来如下:
node1.example.com node2.example.com node3.example.com node4.example.com
其次,你必须设定你的计算机使得登录节点机是不需要密码的(通常指的是从启动MPI应用的节点登录到其它节点是不需要密码的)。这样,运行一个编译好的MPI应用只需要编写一个机器节点文件(比如取名为 machines ),执行命令就如下:
mpirun -np 2 -machinefile machines hello
命令中 -np 2 指的是进程的个数。----译者注
你将会看到一个很上面使用LAM的例子中很相像的输出----可能在输出顺序上会有些不一样。你可能会在很多例子的输出中看到一些有趣的东西。我们会在以后的文章中讲到。