欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MPI并行编程

程序员文章站 2022-07-12 21:36:18
...

 以下代码是在Windows下运行的,Linux下需要修改MPI函数的写法(大小写)

1 helloWorld

了解并熟悉MPI并行程序开发环境,学会并行程序编译方法,并行程序作业提交方法,并行程序运行状态观察与分析方法,本实验的内容主要包括学习如何编译执行MPI程序以及通过较简单的“helloworld.c”程序的实际运行来进一步的熟悉

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//																													
//实验一,本实验的内容主要包括学习如何编译执行MPI程序以及通过较简单的“helloworld.c”程序的实际运行来进一步的熟悉。
//author : 日期: 2019 年 05 月 13 日		
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

int main(int argc, char* argv[])
{
	int myid, numprocs;
	int  namelen;
	char processor_name[mpi_max_processor_name];
	mpi_init(&argc, &argv);//实现对mpi环境的初始化
	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();//结束mpi环境
	return 0;
}

 

2 有顺序的执行进程

进一步理解UNIX或Linux系统进程通信原理,了解机群体系结构,帮助理解MPI并行程序设计中通信子、进程号、消息属性等基本概念。本实验的内容是熟悉六个MPI基本函数,并学会运用这些基本函数编制简单的并行程序。具体要求是改进实验一中的“helloworld.c”程序,使得作业按照进程号顺序显示“hello world”

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//																													
//实验二,改进实验一中的“helloworld.c”程序,使得作业按照进程号顺序显示“hello world”。
//author : 日期: 2019 年 05 月 13 日																			
//																												  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


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

int main(int argc, char * argv[])
{
	int myid, numproc;
	int cnt = 0;
	char hello[] = "Hello World!";
	char processor_name[MPI_MAX_PROCESSOR_NAME];
	char recv[100];

	MPI_Status status;
	MPI_Init(&argc,&argv);//初始化
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);//获取我的进程号
	MPI_Comm_size(MPI_COMM_WORLD, &numproc);//获取总的进程数
	MPI_Get_processor_name(processor_name,&cnt);

	if (numproc == 1)//只有一个进程,直接输出,不需要收发操作
	{
		fprintf(stderr, "Hello World! Process %d of %d on %s\n", myid, numproc, processor_name);//标准错误输出设备
		fflush(stderr);//理论上是不需要的,但是程序运行的时候stderr没有立即输出,所以我刷新了一下缓存,结果就正确了。
	}
	else if (myid == 0)
	{
		fprintf(stderr, "Hello World! Process %d of %d on %s\n", myid, numproc, processor_name);//标准错误输出设备
		fflush(stderr);
		MPI_Send(hello,strlen(hello), MPI_CHAR, 1, 99, MPI_COMM_WORLD);
	}
	else if (myid<numproc&&myid>=1)
	{
		MPI_Recv(recv, 100, MPI_CHAR, myid - 1, 99, MPI_COMM_WORLD, &status);
		int len;
		MPI_Get_count(&status,MPI_CHAR,&len);//获取收到数据的长度
		recv[len] = '\0';//数组后边补上终止标记
		fprintf(stderr, "%s Process %d of %d on %s\n", recv, myid, numproc, processor_name);//标准错误输出设备
		fflush(stderr);
		if(myid!=numproc-1)
			MPI_Send(hello, strlen(hello), MPI_CHAR, myid+1, 99, MPI_COMM_WORLD);
	}
	MPI_Finalize();
	return 0;
}

 

3  发送浮点数和整数进行广播

认识打包函数与广播函数并能熟练使用。打包函数与广播函数是MPI编程中用得很多的两个函数。本实验的具体要求是用多个进程进行模拟。在一个进程处将一个整型和一个浮点型数据进行打包并广播到其余进程,在其余进程处拆包并打印输出,从而可以核对是否与打包前的数据相符

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//实验三,在一个进程处将一个整型和一个浮点型数据进行打包并广播到其余进程,在其余进程处拆包并打印输出,
//从而可以核对是否与打包前的数据相符。
// author : 日期:2019 年 05 月 14 日
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

int main(int argc, char *argv[])
{
	int i_packData;		//要发送的整数
	double d_packData;	//要发送的浮点数
	int myid, numprocs;
	int  namelen;
	char processor_name[MPI_MAX_PROCESSOR_NAME];
	int position = 0, packsize;
	char buff[1000];//定义缓冲区

	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	MPI_Comm_rank(MPI_COMM_WORLD, &myid);
	MPI_Get_processor_name(processor_name, &namelen);
	if (numprocs == 1)
	{
		MPI_Finalize();
		return 0;
	}
	if (0 == myid)
	{
		//输入待发数据的一个整型和一个浮点型(double)
		printf("Please enter a integer number:");
		fflush(stdout);
		scanf("%d",&i_packData);
		fflush(stdout);
		printf("Please enter a  floating number:");
		fflush(stdout);
		scanf("%lf",&d_packData);
		fflush(stdout);
		
		//显示输入的数据
		printf("Input integer number is %d\tInput floating number is %lf\n", i_packData, d_packData);
		fflush(stdout);
		packsize = 0;//记录包的大小
		//打包;
		MPI_Pack(&i_packData, 1, MPI_INT, &buff, 1000, &packsize, MPI_COMM_WORLD);
		MPI_Pack(&d_packData, 1, MPI_DOUBLE, &buff, 1000, &packsize, MPI_COMM_WORLD);
	}
	//广播发送
	MPI_Bcast(&packsize, 1, MPI_INT, 0, MPI_COMM_WORLD); //广播打包数据的大小,刚开始没注意将packsize和position合用,党务了一段时间。(坑之一)
	MPI_Bcast(buff, packsize, MPI_PACKED, 0, MPI_COMM_WORLD); //广播打包的数据
	if (myid > 0)
	{
		//解包
		position = 0;//缓冲区当前位置,字节(整型)
		MPI_Unpack(buff, packsize, &position, &i_packData, 1, MPI_INT, MPI_COMM_WORLD);		//解包出整型数据
		MPI_Unpack(buff, packsize, &position, &d_packData, 1, MPI_DOUBLE, MPI_COMM_WORLD);	//解包出浮点型数据
		//显示解包数据
		fprintf(stderr, "Recive integer number: %d,  Recive floating number:%lf , Process %d of %d on %s\n", i_packData, d_packData, myid, numprocs, processor_name);//标准错误输出设备
		fflush(stderr);
	}
	MPI_Finalize();
	return 0;
}

 

4 多进程求解Pi

通过综合应用各种的MPI函数来解决一个比较实际的问题从而对MPI的并行编程有一个更加深入的理解。具体的要求是分析计算圆周率的计算方法,通过PCMA并行程序设计方法设计并行算法,运用聚合通信设计并行方案,最后编程实现。在分配中要注意平均分配,保证负载均衡;这样使每个进程做完[n/np]+1 个f(xi)计算,并局部求和;最后把所有计算(局部求和)结果求和归约到0号进程,并对其结果除n即可得到pi的计算结果。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//实验四,在一个进程处将一个整型和一个浮点型数据进行打包并广播到其余进程,在其余进程处拆包并打印输出,
//从而可以核对是否与打包前的数据相符。
// author : 日期:2019 年 05 月 14 日
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <mpi.h>
#include <math.h>
long    n,    	/*number of slices        */
		i;    	/* slice counter           */
double sum, 	/* running sum             */
		pi,     /* approximate value of pi */
		mypi,
		x,    	/* independent var.        */
		h;      /* base of slice           */
int group_size, my_rank;


int main(int argc, char *argv[])
{
	int group_size, my_rank;
	double local_start, local_finish, local_elapsed, elapsed;

	MPI_Status status;
	MPI_Init(&argc, &argv);
	local_start = MPI_Wtime();//进程开始计时
	MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
	MPI_Comm_size(MPI_COMM_WORLD, &group_size);

	n = 2000;
	/* Broadcast n to all other nodes */
	MPI_Bcast(&n, 1, MPI_LONG, 0, MPI_COMM_WORLD);
	h = 1.0 / (double)n;
	sum = 0.0;
	for (i = my_rank + 1; i <= n; i += group_size)
	{
		x = h*(i - 0.5);
		sum = sum + 4.0 / (1.0 + x*x);
	}
	mypi = h*sum;
	/*Global sum */
	local_finish = MPI_Wtime();
	local_elapsed = local_finish - local_start;
	MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
	MPI_Reduce(&local_elapsed, &elapsed, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);//将所有进程运行的时间的最大值返回到0号进程。
	printf("%d 号进程运行时间%e\n", my_rank, local_elapsed);
	fflush(stdout);
	if (my_rank == 0) 
	{        
		// 0号进程输出结果和运行的总时间
		printf("pi is approximately : %.16lf\n", pi);
		printf("Run Time: %e\n",elapsed);	//输出运行时间
		fflush(stdout);
	}
	MPI_Finalize();
	return 0;
}