ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

高性能计算之MPI:第一次MPI并行程序设计练习

2021-06-08 19:55:51  阅读:219  来源: 互联网

标签:num 并行程序 rank MPI int 高性能 COMM WORLD


第一次MPI并行程序设计练习

第一题:定积分计算

题目要求:

设计MPI并行程序,计算定积分: f 0 10 x d x f_{0}^{10}\sqrt{x}dx f010​x ​dx。

解题思路:

这题非常简单,首先我们设定有 n 个进程分任务并行完成这个计算需求。每个进程要计算 m 个小曲边梯形的面积。然后规定除了 0 进程以外的进程把计算的结果发给 0 进程,然后再由 0 进程统一把计算的部分结果加起来就是整个定积分的值了!

怎么计算曲边梯形的面积呢?稍微啰嗦一下下,假设当前进程的秩是 rank,那么这个进程处理的区间的左边界一定是:rank * m,然后在该进程下,每次计算的曲边梯形的高 h = f((rank * m + k) * w + w / 2),这个f(x)也就是被积函数!这个 w 也就是每个进程处理的区间的每个小曲边梯形的步长,也就是人家的底边。每次我们计算小曲边梯形的底边中点对应的高,就是 h了!然后累积它们即可!

解题代码:

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

double f(double x) {
	return sqrt(x);// f(x) = sqrt(x)
}

int main(int argc, char** argv) {
	int rank, size;
	double s = 0.0, t = 10.0;
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	int pro_num = size;
	int per_num = 10000, k;
	double w = (t - s) / (1.0 * pro_num * per_num);
	double part_num = 0.0, x;
	for (k = 0; k < per_num; ++k) {
		x = s + (1.0 * rank * per_num + k) * w + w / 2.0;
		part_num += f(x) * w;
	}
	if (0 != rank) {
		MPI_Send(&part_num, 1, MPI_DOUBLE, 0, rank, MPI_COMM_WORLD);
	}
	else {
		int j = 0;
		double result = part_num;
		double temp_res;
		for (j = 1; j < size; ++j) {
			MPI_Recv(&temp_res, 1, MPI_DOUBLE, j, j, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
			result += temp_res;
		}
		printf("The result is : %.3lf\n", result);
	}
	MPI_Finalize();
	return 0;
}

解题结果:

在这里插入图片描述

解题感悟:

这题还是比较easy的,相比课题上那不是人的seidel迭代、超松弛迭代……这个还算非常友好!

不过需要注意的是,这个Linux里头啊,编译带有 math.h 的数学库,需要在后面加一个参数 -lm 来告知编译器你用了 math 库!

第二题:计算圆周率PI(定积分拓展)

题目要求:

想办法计算圆周率PI,要用MPI并行程序设计!

解题思路:

已知 PI = arctan(1) * 4,于是有下列计算PI的公式:

π = 4 ∗ f 0 1 1 1 + x 2 d x \pi = 4 * f_{0}^{1}\frac{1}{1 + x^2}dx π=4∗f01​1+x21​dx

这样就和上面第一题完全一样的啦,就不多解释了,直接上代码吧!

解题代码:

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

double f(double x) {
	// f(x) = 1/(1 + x^2)
	return (1.0 / (1 + x * x));
}

int main(int argc, char** argv) {
	int rank, size;
	double s = 0.0, t = 1.0;
	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	int pro_num = size;
	int per_num = 100000, k;
	double w = (t - s) / (1.0 * pro_num * per_num);
	double part_num = 0.0, x;
	for (k = 0; k < per_num; ++k) {
		x = s + (1.0 * rank * per_num + k) * w + w / 2.0;
		part_num += f(x) * w;
	}
	if (0 != rank) {
		MPI_Send(&part_num, 1, MPI_DOUBLE, 0, rank, MPI_COMM_WORLD);
	}
	else {
		int j = 0;
		double result = part_num;
		double temp_res;
		for (j = 1; j < size; ++j) {
			MPI_Recv(&temp_res, 1, MPI_DOUBLE, j, j, MPI_COMM_WORLD, MPI_STATUSES_IGNORE);
			result += temp_res;
		}
		result *= 4;
		printf("The result is : %.3lf\n", result);
	}
	MPI_Finalize();
	return 0;
}

解题结果:

在这里插入图片描述

解题感悟:

本题的做法理应非常多!
还可以去做级数,因为PI = cos(-1) = ……(级数的表示)
所以还可以分任务,并行计算这个级数,然后近似得到结果!

第三题:环形乒乓球

题目要求:

乒乓球是一项双人运动,要求是A发球,随后B接球……如此循环往复!

要求利用MPI并行程序设计,得到两个计算节点(或者是同一计算节点下的两个进程),并行完成接球与发球,做到两个进程双方来回打乒乓球!一定要是A发完球B才能接球,B发球时A同理!

解题思路:

两方发球接球倒是很简单的事情,只需要两个进程并行完成消息传递即可!

但是后面的要求是要A完成发球B才能接球,由于程序的运行、内存的分配在不同的两个进程中是相互独立的!所以可能出现A一直调度而B比较慢,又无法抢占调度!

出现A连续发球好几次B才接到球这样的情况,所以我们用Sleep()的方式处理,要求A在发球后立刻进入阻塞队列,好让B有机会调度,等到B发球的时候同理!

解题代码:

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

int main(int argc, char** argv)
{
	MPI_Init(NULL, NULL);
	int world_size;
	MPI_Comm_size(MPI_COMM_WORLD, &world_size);
	int world_rank;
	MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
	if(2 != world_size)
	{
		fprintf(stderr, "error: must have two process in %s \n", argv[0]);
		MPI_Abort(MPI_COMM_WORLD, 1);
	}
	int LIMIT = 10, count = 0, tag1 = 0, tag2 = 0;
	while(count < LIMIT)
	{
		if(tag1 == tag2)
                        MPI_Barrier(MPI_COMM_WORLD);
		if(world_rank == 1)
		{
			MPI_Recv(&count, 1, MPI_INT, 0, tag2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
			tag2++;
			printf("1 process received %d from 0 process\n", count);
			sleep(1);
			count++;
			MPI_Send(&count, 1, MPI_INT, 0, tag2, MPI_COMM_WORLD);
		}
		else
		{
			count++;
			MPI_Send(&count, 1, MPI_INT, 1, tag1, MPI_COMM_WORLD);
			tag1++;
			MPI_Recv(&count, 1, MPI_INT, 1, tag1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
			printf("0 process received %d from 1 process\n", count);
			sleep(1);
		}
	}
	MPI_Finalize();
	return 0;
}

解题结果:

在这里插入图片描述

这里我们就可以看到,我们做到了每一轮收到的一方都是上一轮发球的一方!

解题感悟:

自认为这题我做的很糟糕!!!!

因为我采用这种强行阻断进程,强行让进程进入阻塞队列的Sleep()函数,导致了效率非常底下!!!!

希望各位前辈大佬带带刚刚入门高性能计算的小弟,教教我有无高效的做法完成这种来回进程调度!!!!

标签:num,并行程序,rank,MPI,int,高性能,COMM,WORLD
来源: https://blog.51cto.com/u_15262702/2883290

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有