分类: 云计算
2012-07-09 18:16:21
消息传递模型
1、平行计算由许多进程组成,每个都计算一些本地数据。每个进程都有纯粹的本地变量,且没有任何机制可以让任何进程直接访问另一个的内存。
2、进程间的共享通过消息传递发生,也就是通过显式地在进程间发送和接收数据。
注意该模型涉及到“进程”,在理论上它们不需要运行在不同的处理器上。我们这里通常假设不同进程运行在不同的处理器上,所以术语“进程”和“处理器”可以交替使用。
这个模型有用的主要原因是它非常具有一般性。本质上讲,任何类型的并行计算都可以转换成消息传递形式。此外,这个模型:
1、可以在许多不同平台上实现,从共享内存多处理器到工作站构成的网络,甚至可以是单处理器的机器。
2、通过并行应用,通常比诸如共享内存模型的模型允许对数据位置和流动的更多控制。因此程序通过使用显式的消息传递,经常可以达到更好的性能。事实上,性能是为何消息传递不太可能从并行编程世界里消失的原因。
消息传递编程接口MPI(Message Passing Interface)被 试图作为消息传递模型的一个标准实现,是由全世界工业、科研和政府部门联合建立的一个消息传递编程标准,其目的是为了基于消息传递的并行程序设计提供一个 高效、可扩展、统一的编程环境。它是目前最为通用的并行编程方式,也是分布式并行系统的主要编程环境。MPI-1标准定义于1994年春:
1、它分别规范了命名、调用序列、通过Fortran 77和C调用的子例程和函数的结果。MPI的所有实现遵守这些规则,从而保证了兼容性。MPI程序可以在任何支持MPI标准的平台上编译和运行;
2、库详细的实现交给独立的厂商,他们可以自由地针对他们的机器提供优化版本;
3、MPI-1标准的实现对许多平台可用。
MPI-2标准定义了三个单向的通信操作:
1、Put:写入远程内存;
2、Get:读取远程内存;
3、Accumulate:缩减各任务间的相同内存;
MPI-2同时还定义了三种不同的同步方式(全局锁、成对锁、和远程锁)。还有并行I/O、C++和Fortran 90绑定、以及动态进程管理所使用的工具。目前有些MPI实现已经包含了部分MPI-2标准,但完整的MPI-2还不可用。
MPI的主要目标是为了:
1、提供源代码的兼容性。MPI程序应当可以在任何平台上编译和运行;
2、允许不同架构上的高效实现。
MPI也提供了:
1、许多功能,包含许多不同类型的通信、普通“收集”操作的特定指令、和处理用户定义数据类型和拓扑的能力;
2、对异构并行架构的支持。
明显没有包含在MPI-1的有:
1、运行一个MPI程序的精确机制。一般说来,这是平台相关的且你需要翻阅本地文档来找到如何完成这个机制;
2、动态进程管理,也就是在代码运行时改变进程的数量;
3、调试;
4、并行I/O。
使用MPI的时机
当你需要做以下事时应该使用MPI:
1、编写可移植的并行代码;
2、通过并行编程得到高效率,例如编写并行库;
3、处理涉及不适合“数据并行”模型的不规范或动态的数据关系的问题。
以下情况不适合使用MPI:
1、可以通过数据并行(例如High-Performance Fortran)或共享内存的方法(例如OpenMP或基于指令的专利范式)得到足够的性能和可移植性;
2、可以使用已有库里的(本身由MPI实现的)并行例程。
3、根本不需要并行机制!!
消息传递程序的基本特性
消息传递程序由通过函数调用来通信的串行程序的多个实例组成。这些调用大致可以分为四类:
1、用于初始化、管理、最终终止通信的函数;
2、用于处理器对之间通信的函数;
3、执行进程组间通信操作的函数;
4、创建任意数据类型的函数。
第1类函数由以下函数组成:开启通信、标识所使用的处理器的数量、创建子处理器组、以及标识程序的一个特定实例在哪个处理器上运行。
第2类函数被称为点对点通信操作,由不同类型的发送接收操作组成。
第3类函数为收集操作,提供进程组间的同步或特定类型的明确定义的通信操作,并执行通信/计算操作。
MPI系统
除各厂商提供的MPI系统外,一些高校、科研部门也在开发免费的通用MPI系统,其中比较著名的有:
1、MPICH
2、LAM MPI
它们均提供源代码,并支持目前绝大部分并行计算机系统(包括微机和工作站机群)。事实上许多厂商提供的MPI系统是在MPICH的基础上经过针对特定硬件优化形成的。
MPI标准的第一个版本MPI 1.0于1994年公布,最新标准为2.0版,于1998年公布。
一个MPI系统通常由一组库、头文件和相应的运行、调试环境构成。MPI并行程序通过调用MPI库中的函数来完成消息传递,编译时与MPI库链接。而MPI系统提供的运行环境则负责一个MPI并行程序的启动和退出,并提供适当的并行程序调试、跟踪方面的支持。
MPICH是 目前使用最广泛的免费MPI系统,它支持几乎所有Linux/Unix以及Windows 9x、NT、2000和XP系统。利用MPICH既可以在单台微机或工作站上建立MPI程序的调试环境,使用多个进程模拟运行计算环境。事实上,它是运行 在目前大部分机群系统上的主要并行环境。
在Ubuntu的软件中心搜索mpich,可以找到“Development files for MPICH2 (libmpich2-dev)”,安装它可以得到C、C++、和Fortran程序的mpi编译器,即mpicc、mpicxx、mpif77和 mpif90。
第一个程序
#include
#include
void main(int argc, char *argv[]) {
int err;
err = MPI_Init(&argc, &argv);
printf("Hello world!\n");
err = MPI_Finalize();
}
使用命令mpicc first_program.c可以得到可执行文件a.out。
可以看到MPI的函数/子例程的名字都以MPI_开头。同时注意到头文件(mpi.h或mpif.h)包含了MPI的函数原型以及定义。MPI函数会返回一个错误码来表明是否有错误发生。
MPICH 系统使用ch_p4(CHannel_Portable Programs for Parallel Processors)作为底层通信支持。基于ch_p4的MPICH可以像普通UNIX可执行文件一样直接执行,但默认情况下它只启动一个进程。当需要 启动多个进程时,有两个方法来控制启动的进程数目,第一个方法是使用选项-p4pg,第二个方法是利用MPICH提供的一些脚本文件,如mpirun。在单机情况下,最方便的是用命令mpirun来运行MPICH程序。
mpirun最简单、最常用的形式为:
mpirun [-np 进程数] 程序名 [命令行参数]
方括号中为可选参数。
例如:
mpi ./a.out
输出
Hello world!
而mpirun -np 4 ./a.out
输出
Hello world!
Hello world!
Hello world!
Hello world!
点对点通信与消息
MPI里基本的通信是“点对点”通信。也就是两个处理器之间的直接通信,一方发送而另一方接收。
MPI里的点对点通信是双方参与的,也就是说同时需要显式的发送和显式的接收。在没有两个进程同时参与的情况下,数据不能传输。
在通常的发送和接收里,进程间传递由一些块数据组成的消息。一个消息由指明源进程和目标进程的信封和包含要发送的真实数据的主体组成。
MPI使用三部分信息来灵活地描述消息主体:
1、缓冲:内存的起始地址,存储要发送的消息或接收到的消息;
2、数据类型:最简单的例子是基本类型float、int等。更高级的应用里可以是基于基本类型的用户定义类型,类似于C的结构体,但数据可以放置在内存的任何地方,而不必是连续的内存地址。
3、计数器:要发送的数据类型的项数。
注意MPI标准化了基本类型的名称。这意味着我们不必担心异构环境下机器表示的区别。
通信模式和竞争临界区
MPI 提供了很大的灵活性来指明消息如何发送。有多种定义了传送消息的过程的通信模式,以及决定通信事件何时结束的一堆临界区。例如:同步发送被定义为只当目的 地承认接收到消息时才完成。缓冲发送则在数据拷贝到一个(本地)缓冲区时则完成,而不保证消息到达目的地。在所有的情况下,发送的完成都暗示着可以安全覆 盖原有数据的内存区域。
有四种可用的发送通信模式:
1、标准(Standard);
2、同步(Synchronous);
3、缓冲的(Buffered);
4、预备的(Ready)。
对于接收方,只有一种通信模式。当数据真正到达并可用时,接收才算完成。
阻塞与非阻塞通信
阻塞发送/接收直到操作完成才从函数返回。这保证了调用进程继续执行时相关的竞争临界条件已经得到满足。
非阻塞发送/接收立即返回,而不保证竞争条件是否满足。这样的好处是进程可以在后台进行通信,同时还能做其它事情。
集体通信(Collective Communications)
除了点对点通信,MPI还有执行集体通信的函数。它们允许更大的进程组以不同的方式通信,比如一对多或多对一。
相比与点对点通信,使用集体通信函数的好处有:
1、出错的概率大大降低。一行调用集体通信函数的代码,等价于多行点对点通信的调用代码。
2、源代码可读性更高,因而简化了调试和维护。
3、集体通信的优化形式经常比等价的点对点通信更快。
集体通信的例子包括广播操作、收集与散播操作、以及收缩操作。
广播操作(Broadcast Operation)
最简单的集体操作类型。单个进程把一些数据的拷贝发送给一个组的所有其它进程。多个接收进程都收到相同数据。
收集与散播操作(Gather and Scatter Operations)
可能是最重要的收集操作类型。把一个进程的数据分发到一组进程,或反之。MPI提供了两种收集与散播操作,即数据均匀或不均匀地跨进程分发。
在散播操作中,所有的数据(某种类型的数组)初始由单个进程收集,之后数据的各个片段被分发到不同的进程上。这些数据片段可能不是均匀分割的。收集操作是散播操作的逆操作:把分布在多个进程上的数据片段以恰当的顺序聚集到单个进程上。
收缩操作(Reduction Operations)
该操作里,单个进程(根进程)从某个组里的其它进程收集数据,并把它们合并成单个数据项。例如,使用收缩操作来求一个数组分布到各个进程上的元素的和。其它的例子还有求最大值、最小值、各种逻辑或位运算等等。