MPI学习

  前段时间学习了OpenMP,并且很容易地将其用到了实际应用中,但OpenMP也有很多缺点:所有线程共享内存空间,硬件制约较大;目前主要针对循环并行化。为了进一步了解并行计算,MPI是一个不得不接触的东西。它的优点有:无论硬件是否共享内存空间,都可以使用;与OpenMP相比,可以处理规模更大的问题;每个线程都有自己的内存和变量,这样不用担心冲突问题。MPI的缺点有:算法上经常有较大改动;较难使用;性能上会受到通信网络的影响。

1.MPI简介

MPI是一个跨语言的通讯协议,用于编写并行计算机,其支持点对点和广播。MPI是一个消息传递应用程序接口,包括协议和语义说明,它们指明其如何在各种实现中发挥其特性,目前与MPI绑定的语言只有FORTRAN和C/C++。MPI的目标是高性能,大规模行和可移植性,是一种基于信息传递的并行编程技术。消息传递接口是一种编程接口标准,而不是一种具体的编程语言。简而言之,MPI标准定义了一组具有可移植性的编程接口。

2.MPI环境配置

首先是MPI的下载,最新的MPI已经由微软进行托管,网址如下:http://www.mpich.org/downloads/,来到如下界面,点击http进入。

找到MS-MPI Download部分,选择最新的进行下载:

将两个都选上进行下载:

接下来就是MPI环境的配置,以VS2015为例。首先打开VS,选择文件 — 新建 — 项目 — Visual C++ — win32控制台应用程序。

创建成功后,会有一个带有简易C++程序的完整项目,然后点击项目—右键—属性,就进入到了我们需要配置库文件的地方。

首先在跳出的界面中选择平台为X64。(最好改为Debug模式来编译运行,因为在后面的代码测试时,release模式下总是会出现无法打开文件msmpi.lib)

进入VC++目录,VC++目录→包含目录,添加:KaTeX parse error: Undefined control sequence: \Microsoft at position 10: (MPI安装位置)\̲M̲i̲c̲r̲o̲s̲o̲f̲t̲ ̲SDKs\MPI\Includ…(MPI安装位置)\Microsoft SDKs\MPI\Lib\x64;其中,$(MPI安装位置)为你安装MPI的位置,如:C:\Program Files (x86)。
在这里插入图片描述
选择C/C++ — 预处理器 — “编辑”,添加:MPICH_SKIP_MPICXX

C/C++ →代码生成→运行库,选择:多线程调试(/MTd)。

链接器→输入→附加依赖项,添加:msmpi.lib,就此完成了MPI环境的配置。

3.基础知识

①进程

通俗的说,进程就是运行的程序。一个程序可以含有多个进程,但一个进程不能同属于多个程序。进程拥有独立的运行环境(内存、寄存器、CPU执行时间等),是操作系统中独立存在的可执行的基本单位。每个进程所占有的资源都是独立的,不与其他的进程共享,不能访问其他进程内存空间,其他进程也无法访问该进程内存空间,但可以通过消息传递来进行通信。

②进程组

指一个MPI程序中的所有进程(n个)的集合。该程序中所有进程编号从0到n-1,主要是为了标识不同的进程,可以通过进程的编号来索引该进程。不同进程组的进程的编号可以相同。

③通信器(MPI_Comm)

④ 消息

需要通信的数据

⑤mpi对象

MPI内存的数据结构,包括数据类型(MPI_DOUBLE),通信器(MPI_COMM)等。

⑥mpi数据类型

MPI类型c类型
MPI_CHARsigned char
MPI_SHORTsigned short int
MPI_INTsigned int
MPI_LONGsigned long
MPI_UNSIGNED_CHARunsigned char
MPI_UNSIGNED_SHORTunsigned short int
MPI_UNSIGNEDunsigned int
MPI_UNSIGNED_LONGunsigned long int
MPI_FLOATfloat
MPI_DOUBLEdouble
MPI_LONG_DOUBLElong double

4.MPI基本函数

① int MPI_Init(int argc,char ** argv[])

该函数通常应该是第一个被调用的MPI函数用于并行环境初始化,其后面的代码到MPI_Finalize()函数之前的代码在每个进程中都会执行一次。(MPI系统将通过argc,argv得到命令行参数,所以main函数必须带参数,否则会报错)

② int MPI_Finalize(void)

该函数的功能是退出MPI系统,所有进程正常退出都必须调用。表明并行代码的结束,结束除主进程外其他进程。(串行代码仍可在主进程(rank = 0)上运行,但不能再有MPI函数)

① int MPI_Comm_size(MPI_Comm comm,int * size)

该函数的功能是获得进程个数size。(指定一个通信子,也指定了一组共享该空间的进程,这些进程组成该通信子的group,该函数是获取通信子comm中规定的group包含的进程的数量)

④ int MPI_Comm_rank(MPI_Comm comm,int * rank)

该函数的功能是得到本进程在通信空间中的rank值,即在group中的逻辑编号(该rank值为0到p-1间的整数,相当于进程的ID)

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

该函数为阻塞型发送函数,即等消息发送后才可返回执行下一句。buf为发送区缓存。count为发送的数据的大小。datatype为发送的数据类型。dest为发送的目标进程编号。tag为标志,该标志必须和接收函数的标志一致时才可被接收函数接收。comm通信器。可以理解为buff,count,datatype为消息数据,dest,tag,comm为消息信封。

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

该函数为阻塞型接收函数,即接收到消息后才可返回执行下一句。buf为接收区缓存,接收区缓存必需大于等于发送的数据。count为接收的数据大小。datatype为接收的数据类型。source为数据的发送源进程编号。tag为标志,该标志必须与发送函数的tag一致才会被接收。comm为通信器。status返回接收状态。

(前六个为最基本的MPI函数,后续的也是一些常用的MPI函数)

int MPI_Initialized(int flag)
该函数用来检测mpi环境是否初始化,唯一可在MPI_Init前使用的函数。

int MPI_Isend(const void * buf, int count, MPI_Datatype datatype, int dest, int tag,MPI_Comm comm, MPI_Request * request);
int MPI_Irecv(void * buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request * request)
为对应的发送接收函数的非阻塞型函数,即当函数执行后立马返回执行下一行代码,而不需要等待数据正式发送或接收后才可返回。这经常会导致数据的混乱,因此使用非阻塞发送和接收时格外需要注意。

int MPI_Abort(MPI_Comm comm, int errorcode)
异常终止函数。在出现了致命错误而希望异常终止MPI程序时执行,MPI系统会设法终止comm通信器中所有进程, 输入整型参数errorcode,将被作为进程的退出码返回给系统。

int MPI_Get_processor_name(char * name,int * resultlen)
获取处理器名称,在返回的name中存储所在处理器的名称,resultlen存放返回名字所占字节,应提供参数name不少于MPI_MAX_PRCESSOR_NAME个字节的存储空间

int MPI_Get_version(int * version,int * subversion)
获取mpi版本号,若版本为4.0,则version为4,subversion为0。

double MPI_Wtime(void)
返回调用时刻的墙上时间,用浮点数表示秒数

5.MPI简单示例

#include<stdio.h>
#include<string.h>
#include"mpi.h"

int main(int argc,char *argv[])
{
	int numprocs, myid, source;
	MPI_Status status;
	char message[100];
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	if (myid != 0) {
		//非0号进程发送消息
		strcpy(message, "Hello world");
		MPI_Send(message, strlen(message) + 1, MPI_CHAR, 0, 99, MPI_COMM_WORLD);
	}
	else
	{
		for (source = 1; source < numprocs; source++) {
			MPI_Recv(message, 100, MPI_CHAR, source, 99, MPI_COMM_WORLD, &status);
			printf("接收到第%d号进程发送的消息:%s\n", source, message);
		}
	}
	MPI_Finalize();
    //return 0;
}

在这里插入图片描述

6.MPI模型基本结构

//通过上面的代码,我们可以得出MPI模型的基本结构
#include"mpi.h"
int main(int argc,char *argv[])
{
	int rank,size;
	//初始化MPI环境
	MPI_Init(&argc, &argv);
	//获取进程组中进程编号rank
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	//获取进程组中进程数size
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	......
	//各种操作
	......
	//退出MPI环境
	MPI_Finalize();
}
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值