MPI ( Message Passing Interface) 是消息传递函数库的标准规范,是一种消息传递模型,主要应用于分布式数据存储环境下的进程之间的通信,它不是一种语言,在 Fortran 和C 中都可以对它的接口函数进行调用。
MPI 的优势体现在高可移植性,使用 MPI 作消息传递的 C 或 Fortran 并行程序可不加改变地运行在IBM PC、MS Windows、Unix 工作站、以及各种并行机上。
所以在消息传递机制MPI 环境下通过数值计算方法计算π的值来观察运行时间
一、代码实现
#include "mpi.h"
#include <iostream>
#include <fstream>
#include <math.h>
#include <boost/multiprecision/cpp_bin_float.hpp>
using namespace boost::multiprecision;
#define PRECISION 2000
typedef number<cpp_bin_float<PRECISION> > pi_type;
typedef int a_type;
pi_type f(a_type a)
{
return ((a & 1) ? pi_type(16) : pi_type(-16)) / (a + a - 1) / pow(pi_type(5), a + a - 1) - ((a & 1) ? pi_type(4) : pi_type(-4)) / (a + a - 1) / pow(pi_type(239), a + a - 1);
}
pi_type pi_exact;
int main(int argc, char* argv[])
{
std::ifstream fin();
char buf[PRECISION + 2];
fin.get(buf, PRECISION + 1);
pi_exact = pi_type(buf);
// 从文件读入Pi的精确值,用于计算精度。
int myid, numprocs, done = 0, namelen;
double startwtime = 0.0, endwtime;
char processor_name[MPI_MAX_PROCESSOR_NAME];
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);
fprintf(stdout, "Process %d of %d is on %s\n",
myid + 1, numprocs, processor_name);
fflush(stdout);
while (!done) {
a_type n, i;
if (myid == 0) {
fprintf(stdout, "Enter the number of iterations: (0 quits) ");
fflush(stdout);
std::cin >> n;
startwtime = MPI_Wtime();
}
MPI_Bcast(&n, sizeof(n), MPI_BYTE, 0, MPI_COMM_WORLD);
if (!n)
done = 1;
else {
pi_type pi = 0, sum = 0;
for (i = myid + 1; i <= n; i += numprocs) {
sum += f(i);
}
pi_type* in = NULL;
if (myid == 0) {
in = (pi_type*)malloc(sizeof(pi_type) * numprocs);
}
MPI_Gather(&sum, sizeof(sum), MPI_BYTE, in, sizeof(sum), MPI_BYTE, 0, MPI_COMM_WORLD);
if (myid == 0) {
for (int i = 0; i < numprocs; i++) {
pi += in[i];
}
std::cout << std::defaultfloat << std::setprecision(PRECISION);
std::cout << "pi is approximately " << pi << std::endl;
std::cout << std::scientific << std::setprecision(5);
std::cout << "Error is " << boost::multiprecision::fabs(pi - pi_exact) << std::endl;
/*printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - pi_exact));*/
endwtime = MPI_Wtime();
printf("wall clock time = %f\n", endwtime - startwtime);
fflush(stdout);
}
}
}
MPI_Finalize();
return 0;
}
二、运行结果截图
三、实验分析
MPI与0penMP是并行计算领域中最为流行的编程模型。MPI主要针对粗粒度级别的并行,OpenMP主要针对细粒度的循环进行并行,即在循环中将每次循环分配给不同的线程去执行。由于使用线程间共享内存的方式协调并行计算,它在多核/多CPU结构上的效率很高、内存开销小、编程语句简洁直观,因此编程容易、编译器实现也容易。
private子句将一个或多个变量声明为线程的私有变量。每个线程都有它自己的变量私有副本,其他线程无法访问。即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。
shared子句将变量列表中一个或多个变量声明为线程组中子线程共享的变量所谓变量共享,是指在一个并行区域的线程组内,所有线程只拥有该变量的一个内存地址,所有线程对共享变量的访问即是对同一地址的访问。
default子句用来控制并行区域内变量的共享属性,其取值有shared和none两个。指定为shared表示在没有显式指定访问权限时,传入并行区域内的变量访问权限为shared;指定为none意味着必须显式地为这些变量指定访问权限。default子句用来控制并行区域内变量的共享属性,其取值有shared和none两个。指定为shared表示在没有显式指定访问权限时,传入并行区域内的变量访问权限为shared;指定为none意味着必须显式地为这些变量指定访问权限。
在某些情况下,OpenMP默认变量访问权限会导致一些问题,如需要private访问权限的数组被默认成shared了。故建议显式地使用default(none)来去掉变量的默认访问权限。
Parallel for指令之后的结构化块必须是for循环,OpenMP只会并行化那些确定迭代次数的for循环,不会并行while或者do…while循环。
每次循环是依靠数组,收敛的速度还可以,但仍然是有上升的空间。收敛的速度还是和计算的公式有很大的关系。