本篇博客只是介绍mpich命令的使用,并没有涉及并行的底层原理,对应的版本为mpich-3.3.2,但相关命令对其他版本也适用
前言
MPI(Message Passing Interface)是一个消息传递接口的标准,是目前最重要的一个基于
消息传递
的并行编程工具,有多种不同的免费、高效、实用的实现版本,如
openmpi
、
mpich
、
lam
、
chimp
等。MPI以语言独立的形式来定义接口库,并实现了与C、C++、Fortan的绑定。
一、查看mpich版本及配置
mpichversion
:查看mpich的版本及其配置。使用如下
二、编译和链接
使用mpich自带的脚本进行编译和链接,即
mpicc
、
mpicxx
、
mpifort
,分别对应使用C、C++、Fortran进行编写的程序。
例如,假设有一个C++程序
cpi.cpp
,则可以使用
mpicxx
进行编译的示例如下:
mpicxx -o cpi cpi.cpp
下面主要介绍
mpicxx
的命令行参数:
三、运行并行程序
MPI标准建议使用
mpiexec
运行MPI程序,MPICH实现了mpiexec标准并且对其进行了扩展。运行
mpiexec
前一定要先使用
mpicc
、
mpicxx
、
mpifort
生成可执行程序。
-
在本地机器上运行具有
number
个进程的程序:
mpiexec -n <number> ./a.out
-
在多个节点上运行
number
个进程的程序:
mpiexec -f machinefile -n <number> ./a.out
四、6个基本mpi函数
-
int MPI_Init(int *argc, char ***argv)
MPI程序通过调用MPI_Init函数进入MPI环境,并完成所有的初始化工作。 -
int MPI_Finalize(void)
MPI程序通过调用MPI_Finalize函数从MPI环境中退出。 -
int MPI_Comm_rank(MPI_Comm comm, int *rank)
MPI程序通过调用MPI_Comm_rank函数获取当前进程在指定的通信域中的编号,该编号可以将自身和其他进程区分开来,从而实现不同进程之间的并行与合作。 -
int MPI_Comm_size(MPI_Comm comm, int *size)
MPI程序通过调用MPI_Comm_size函数获取指定通信域的进程的个数,进程可根据进程数来确定自己应该完成的任务比例。 -
int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
该函数将起始地址为
buf
的
count
个
datatype
类型的数据发送给目标进程,目标进程在通信域
comm
中的编号由
dest
标识,同时发送方在发送该消息时给定的标签
tag
用于把本次发送的消息和本进程发送给同一目标进程的其他消息区分出来。 -
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
该函数从源进程接收一个消息,并且该消息的标签应该和参数
tag
一致,源进程在通信域中的编号由
source
标识。消息接收后存放在起始地址为
buf
的接收缓冲区,该缓冲区由
count
个
datatype
类型的连续数据元素组成。接收到的消息长度必须小于或等于接收内存区域的长度,因为如果接收到的消息过大,MPI并不截断,从而导致缓冲区溢出。
五、 一个简单的MPI程序(C++实现)
// 文件名 ex.cpp (可以随便取,下面运行时对应好即可)
#include <iostream>
#include <mpi.h>
int main(int arg, char **argv)
{
int rank, size, tag = 1;
int senddata, recvdata;
MPI_Status status;
MPI_Init(&arg, &argv); //MPI初始化
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // 该进程在MPI_COMM_WORLD通信域中的编号
MPI_Comm_size(MPI_COMM_WORLD, &size); // 通信域MPI_COMM_WORLD中总的进程数目
if (rank == 0)
{
senddata = 9999;
MPI_Send(&senddata, 1, MPI_INT, 1, tag, MPI_COMM_WORLD); //进程0发送数据到进程1
}
else if(rank == 1)
{
MPI_Recv(&recvdata, 1, MPI_INT, 0, tag, MPI_COMM_WORLD, &status); //进程0接收进程0发送的数据
std::cout << recvdata << std::endl;
std::cout << "数据发送方:" << status.MPI_SOURCE << "\n消息的标签:" << status.MPI_TAG << "\n接收操作的错误代码" << status.MPI_ERROR << std::endl;
}
MPI_Finalize(); //MPI结束函数
return 0;
}
程序说明:
-
mpi.h
头文件包含MPI函数和数据类型的定义。 - 该程序包括使进程0向进程1发送一个整数类型的数据,进程1接收该数据并打印。
-
MPI_COMM_WORLD
是MPI标准预定义的通信域,所有的MPI进程都属于该通信域。 -
MPI_Status
是MPI中定义的类型数据,分别有三个域,可以通过status.MPI_SOURCE,status.MPI_TAG和status.MPI_ERROR的方式调用这三个信息,其中status已经定义为MPI_Status变量。这三个信息分别返回的值是所收到数据发送源的进程号,该消息的tag值和接收操作的错误代码。 - 运行该程序方式如下(以Linux终端为例),进程数可以指定为大于等于2的任一整数:
mpicxx -o ex ex.cpp
mpiexec -n 2 ./ex
mpiexec -n 5 ./ex
总结
本文仅仅简单介绍了mpich的使用,安装部分可以自行解决(后面有空也可以写写)。此外还介绍了MPI标准的6个基本函数,这在所有MPI的具体实现上都是一致的,如openmpi。最后一个简单的MPI程序介绍了6个基本函数的使用。
参考资料
- 《并行计算:结构·算法·编程》陈国良编著.—3版.—北京高等教育出版社
-
两小时入门MPI与并行计算系列链接