MPI(Message Passing Interface)是一种用于编写并行程序的标准和库,用于在分布式内存系统中进行消息传递和并行计算。MPI提供了一组函数和语义,用于在多个进程之间进行通信和同步,以实现并行计算和并行任务的协调。MPI在高性能计算领域被广泛应用,用于开发并行和分布式内存计算应用程序。它提供了丰富而强大的功能,使得开发者能够充分利用并行计算资源,提高计算效率和性能。MPI有很多种实现。MPICH是其中的MPI实现之一。
MPICH官网:https://www.mpich.org/downloads/
tar -zvxf mpich-4.1.2.tar.gz
cd mpich-4.1.2
./configure --disable-fortran
make -j 10
sudo make install
在源码包的examples文件夹下有测试程序,可进行编译测试:
#include <stdio.h>
#include "mpi.h"
int main(int argc, char *argv[])
{
int rank;
int size;
MPI_Init(0, 0);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("Hello world from process %d of %d\n", rank, size);
MPI_Finalize();
return 0;
}
mpicc hellow.c -o hellow
mpiexec -n 5 ./hellow
如果安装了anaconda,出现/home/fakerth/anaconda3/bin/mpicc: 行 323: x86_64-conda-linux-gnu-cc: 未找到命令,
使用conda deactivate退出anaconda环境,再进行编译。
函数原型:MPI_Init(int *argc, char ***argv)
功能:初始化MPI环境,必须在所有MPI函数之前调用。
参数:argc和argv是main函数的参数,用于传递命令行参数。
注意事项:每个进程都需要调用MPI_Init函数,通过MPI_COMM_WORLD通信域进行初始化。
函数原型:MPI_Finalize()
功能:终止MPI环境,必须在程序结束前调用。
注意事项:每个进程都需要调用MPI_Finalize函数,通过MPI_COMM_WORLD通信域进行终止。
函数原型:MPI_Comm_size(MPI_Comm comm, int *size)
功能:获取指定通信域中的进程数量。
参数:comm是通信域,通常使用MPI_COMM_WORLD表示全局通信域;size是一个指针,返回通信域中的进程数量。
函数原型:MPI_Comm_rank(MPI_Comm comm, int *rank)
功能:获取当前进程在指定通信域中的进程ID(排名)。
参数:comm是通信域,通常使用MPI_COMM_WORLD表示全局通信域;rank是一个指针,返回当前进程在通信域中的进程ID。
函数原型:MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
功能:将数据发送给其他进程。
参数:buf是发送缓冲区的起始地址;count是发送的数据个数;datatype是发送的数据类型;dest是目标进程的ID;tag是消息标签;comm是通信域。
函数原型:MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
功能:接收来自其他进程发送的数据。
参数:buf是接收缓冲区的起始地址;count是接收的数据个数;datatype是接收的数据类型;source是源进程的ID;tag是消息标签;comm是通信域;status是一个结构体指针,用于返回接收消息的状态信息。
#include <iostream>
#include "mpi.h"
using namespace std;
int main(int argc, char**argv)
{
//mpirun -np 4 calculatePI.o 800 其中的800是以参数的形式传入的,位于argv[1]
long double pi=0, answer=0, PI=3.141592653589793238462643383279;
int size, id, namelen,n=1000;
double time;
char processor_name[MPI_MAX_PROCESSOR_NAME];
MPI_Status status;
//如果参数列表中制定了n的值,则将该值赋给n
if(argc==2)sscanf(argv[1], "%d", &n);
MPI_Init(&argc, &argv);
//开始计时
time=MPI_Wtime();
//获取进程信息
MPI_Comm_size( MPI_COMM_WORLD , &size);
MPI_Comm_rank( MPI_COMM_WORLD , &id);
//比较n和size大小,若n过小,则返回
if(n<size)
{
cout<<"输入n值过小, 请重新输入"<<endl;
MPI_Finalize();
return 0;
}
for(int i=id; i<=n; i+=size)
{
long double tempans;
tempans = (long double)1/(long double)(2*i+1);
if(i%2==0)
{
answer+=tempans;
}
else
{
answer-=tempans;
}
}
//如果是主进程
if(id==0)
{
long double recvbuf;
pi=answer;
for(int i=1; i<size; i++)
{
MPI_Recv( &recvbuf , 1 , MPI_LONG_DOUBLE , MPI_ANY_SOURCE , 0 , MPI_COMM_WORLD , &status);
pi+=recvbuf;
}
}
else//发送消息并退出程序
{
MPI_Send( &answer , 1 , MPI_LONG_DOUBLE , 0 , 0 , MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
pi*=4;
cout<<"主进程使用"<<MPI_Wtime()-time<<"秒, 最后得到PI的计算结果为: ";
printf("%.20Lf\n",pi);
cout<<"n = "<<n<<" 使用了 "<<MPI_Wtime()-time <<"s pi = ";
printf("%.20Lf %.20Lf \n",pi,abs(PI-pi));
MPI_Finalize();
return 0;
}
C++用mpicxx编译,感受一个进程和八个进程的计算时间: