0%

First Allreduce Application

MPI安装

Linux安装MPICH

MPICH是MPI的一个使用的非常广泛的库。简单的在linux中sudo apt-get install就可以安装成功,当然也可以下载MPICH源码,可参考MPICH安装

1
2
sudo apt-get update
sudo apt-get install mpich

通过在程序中添加

1
#include<mpi.h>

即可调用MPICH,可以参考这里写第一个简单的MPI Hello World程序。
mpicc和mpicxx是MPICH的编译器,看名字就知道分别对应C和C++程序。简单使用

1
mpicc helloworld.c -o helloworld

就可以编译程序,再使用

1
mpirun -n 4 helloworld

就可以运行mpi程序,mpiexec和mpirun是MPICH提供的两个解释器,能够运行我们编写的应用程序,其中mpirun只能使每个进程运行同一个程序,而mpiexec能使不同进程运行不同的程序,如

1
mpiexec -n 1 program1 : -n 2 porgram2

Windows安装MPI

下载MPICH

MPICH官网下载源码包,选择Windows系统,然后在这里点击http。
再选择这里的安装:

把两个安装程序都装上:

分别点击两个安装程序,生成如下两个文件夹(可以放在D盘,自己命名文件夹):

在Visual Studio中配置环境

在Visual Studio中“创建新项目”->“控制台应用”。我得项目名字叫“MPITest”,然后选择“调试”->“调试属性”。

选择“C/C++”->“预处理器”->“预处理器定义”->添加“MPICH_SKIP_MPICXX”

选择“代码生成”->“运行库”->“多线程调试\MTD”:

选择“输入”->“添加附加依赖项”->添加“msmpi.lib”

选择“VC++目录”->“包含目录”->添加“(MPI安装位置)\Microsoft SDKs\Include”;

再选择“库目录”->添加“(MPI安装位置)\Microsoft SDKs\Lib\x64”

配置完成。

运行方法

使用cmd终端或者Powershell终端运行

按照下一节的Allreduce积分程序写好代码,然后VS里点击运行,生成.exe文件,由于VS的默认进程数是1,只能运行进程数为1的情况。

打开cmd终端,在.exe文件的目录下输入指令

1
mpiexec -n 4 MPITest.exe

得到结果:

在VS里配置命令

复制MPITest.exe程序到C盘,然后点击“MPITest项目属性”->“调试”,配置命令行属性如图即可。注意命令参数是“mpiexec.exe”程序所在的文件夹,在最开始生成的两个安装文件包的“Microsoft MPI”里,并且MPITest.exe程序一定要拷贝到C盘!!!不然VS会报错找不到这个MPITest.exe程序,不知道为什么会有这个bug。

First Allreduce Program

这里展示调用MPI_ Allreduce函数来使用梯形积分法求解
Allreduce操作是MPI中最常用的集合通信操作,与之相似的是Reduce操作,假设有p个进程,每个进程都持有一个含n个元素的向量,所有的p个进程将自己的向量发送给根进程,根进程收集这些向量计算规约的结果(求和、求最大最小值等等),Reduce操作结果保存在根进程,Allreduce则将根进程的结果再广播出去。简单的在应用程序中调用MPI_Allreduce就可以完成上述例程,函数定义如下:
程序可以表示为:

1
2
3
4
5
6
7
int MPI_Allreduce(
const void *sendbuf, //存放源数据
void *recvbuf, //存放规约结果
int count, //数据个数
MPI_Datatype datatype, //数据类型
MPI_Op op, //规约操作类型
MPI_Comm comm); //一组通信进程

MPI_Reduce 和MPI_Allreduce例程的图解如下所示:

图片源网址和MPI_Allreduce的入门教程在这里:英文版中文版

利用梯形积分法求解的思路是将区间[1,e]分成1024个段,一个段对应一个狭小的梯形,面积为,然后将这些梯形相加。MPI可以创建多个进程来加速,每个进程计算一部分区间的积分,然后将结果相加起来。在程序中a和b是整个积分的区间,local_a和local_b是每个进程负责的区间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<stdio.h>
#include<math.h>
#include<mpi.h>

#define e exp(1)

int n = 1024, local_n;
double a = 1.0, b = e, len;
double local_a, local_b, sum = 0.0, ans, local_ans;

double calc(double local_a, double local_b, int local_n)
{
double sum = (log(local_a) + log(local_b)) / 2;
int i;
double xi;
for (i = 1; i < local_n; i++)
{
xi = local_a + len * i;
sum += fabs(log(xi));
}
return sum * len;
}

int main()
{
int comm_size;
int my_rank;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

len = (b - a) / n; //就是dx
local_a = a + my_rank * len * (n / comm_size); //local_a是每个进程负责计算的区间的起始
local_b = local_a + len * (n / comm_size); //local_b是每个进程负责计算的区间的结束
local_n = n / comm_size;

//每个进程计算自己区间上的积分
local_ans = calc(local_a, local_b, local_n);
//每个进程计算的结果local_ans相加到全局结果ans上
MPI_Allreduce(&local_ans, &ans, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

printf("My rank: %d, Result : %.5f\n", my_rank, ans);
MPI_Finalize();

return 0;
}

在Linux中编译时链接上lm数学运算库:

1
2
mpicc allreduce.c -o allreduce -lm
mpiexec -n 4 allreduce

即可得到结果。