MPI学习笔记(一):初识MPI
参考自:https://www.cnblogs.com/hantan2008/p/5452353.html#3697454
1. 安装:
Ubuntu 里似乎很简单
sudo apt-get install mpich
2. 编写 mpi 程序
#include<iostream> using namespace std; #include"mpi.h" #include<cmath> int main(int argc, char ** argv){ int myid, numprocs; int namelen; char processor_name [ MPI_MAX_PROCESSOR_NAME ]; MPI_Init( &argc, &argv); 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(); cout<<" argc = "<<argc<<endl; int n=0; for(int i=0;i<argc;i++){ cout<<"i="<<i<<" argv[i] = "<<argv[i]<<endl; } return 0; }
需要包含 "mpi.h"
其中,MPI_Init( int * argc_p, char *** argv_p ) 是初始化函数,叫电脑知道,做好 mpi 相关的初始化。传入的 argc_p, argv_p 是指向 main 函数的命令行参数 argc, argv 的指针。如果没有命令行参数,也可以输入 NULL。
MPI_COMM_WORLD:通讯子,“一组可以互发消息的进程集合”
int MPI_Comm_rank( __in MPI_Comm comm, __out int * rank ) :第一个参数是输入参数——通信子,第二个参数是输出参数——进程号。
int MPI_Comm_size( __in MPI_Comm comm, __out int * size ):第一个参数是输入参数——通信子,第二个参数是输出参数——通信子中总进程数。
MPI_MAX_PROCESSOR_NAME:似乎是一个整数,值为128.
MPI_Get_processor_name( ..):得到处理器的名字
int MPI_Send (void *buf, int count, MPI_Datatype datatype,int dest, int tag,MPI_Comm comm):点对点发送消息。
*buf: 发送缓冲区,指向发送的变量地址
count: 发送的数据个数
datatype:发送的数据类型
dest: 目标进程序号,取值为 0 到 np-1 的整数
tag: 消息标签,取值为 0 到 MPI_TAG_UB
comm:通信器
int MPI_Recv (void *buf, int count, MPI_Datatype datatype,int source, int tag, MPI_Comm comm,MPI_Status *status):点对点接收消息。
与 MPI_Send 相似,不同的是 MPI_Status * status 为接收状态。MPI_Status 似乎是 MPI 自己定义的一个类,估计手册上有。
3. 编译 mpi 并行程序
mpicxx main.cpp
4. 运行 mpi 并行程序
mpirun -np 2 ./a.out
其中,np 表示进程数。
得到结果为
Hello World! Processor 0 of 2 on DESKTOP_1IJIBGL Hello World! Processor 0 of 2 on DESKTOP_1IJIBGL argc = 1 i=0 argv[i] = ./a.out argc = 1 i=0 argv[i] = ./a.out
令我不解的是,代码中 MPI_Finilize(); 后面的内容也被执行了两遍。
5. 点对点通信
#include<iostream> using namespace std; #include"mpi.h" #include<cmath> int main(int argc, char ** argv){ int myid, numprocs; int namelen; char processor_name [ MPI_MAX_PROCESSOR_NAME ]; int ha; int i; int total = 0; MPI_Init( &argc, &argv); MPI_Comm_rank( MPI_COMM_WORLD, & myid); MPI_Comm_size( MPI_COMM_WORLD, & numprocs); if( myid !=0 ){ ha = myid; MPI_Send( &ha, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); } else{ for( i = 1; i < numprocs; i++){ MPI_Recv( &ha, 1, MPI_INT, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); total += ha; } cout<< " total = "<<total<<endl; } MPI_Finalize(); return 0; }
编译上面的文件以后,运行
mpirun -np n ./a.out
则会得到 n(n-1)/2
进程 1,2,..., np-1 会向进程 0 发送 其序号的值,而进程 0 将收到的这些值全部加起来。
6. 集体通信 (collective communications)
包括各种 点对多,多对点,多对多,可参考第一排的链接,或者官网上的 documentation,我还没搞到一本比较全的 documentation。
这里先试用一下 int MPI_Reduce (void *sendbuf, void *recvbuf, int count,MPI_Datatype datatype, MPI_Op op, int root,MPI_Comm comm)
op 可以是 MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD 等等。root 为根进程的序号。
#include<iostream> using namespace std; #include"mpi.h" #include<cmath> int main(int argc, char ** argv){ int myid, numprocs; int namelen; char processor_name [ MPI_MAX_PROCESSOR_NAME ]; int ha; int i; int total = 0; MPI_Init( &argc, &argv); MPI_Comm_rank( MPI_COMM_WORLD, & myid); MPI_Comm_size( MPI_COMM_WORLD, & numprocs); MPI_Reduce( & myid, & total, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD ); if( myid ==0 ){ cout<<" total = "<<total<<endl; } MPI_Finalize(); return 0; }
和之前的程序功能一样,都是计算 n(n-1)/2,但是一行就搞定了。