好了这次叫我们来看看什么是mpi。
那好了,让我们来以一张图来看看它到底是个什么东西吧。
这里其实我们不难发现,对于串行而言,内存与CPU处理器之间是相连的,但是,对于mpi而言,对每一个处理器,都是有各自的内存,不过它们之间可以通过网络的方式连接起来,构成共享。
再来介绍介绍,使用mpi不可或缺的调用接口吧,来看看万能的度娘怎么解释的。
1.mpi init初始化MPI执行环境,建立多个MPI进程之间的联系,为后续通信做准备
2.mpi finalize结束MPI执行环境
3.mpi comm rank用来标识各个MPI进程的,给出调用该函数的进程的进程号,返回整型的错误值。两个参数:MPI_Comm类型的通信域,标识参与计算的MPI进程组; &rank返回调用进程中的标识号;
4.mpi comm size用来标识相应进程组中有多少个进程
5.mpi send(buf,counter,datatype,dest,tag,comm): buf:发送缓冲区的起始地址,可以是数组或结构指针;count:非负整数,发送的数据个数;datatype:发送数据的数据类型;dest:整型,目的的进程号;tag:整型,消息标志;comm:MPI进程组所在的通信域
含义:向通信域中的dest进程发送数据,数据存放在buf中,类型是datatype,个数是count,这个消息的标志是tag,用以和本进程向同一目的进程发送的其它消息区别开来
6.mpi recv(buf,count,datatype,source,tag,comm,status): source:整型,接收数据的来源,即发送数据进程的进程号; status:MPI_Status结构指针,返回状态信息
好的,看完上面的介绍,我们来一起看看这程序要如何写是好呢。
先让我们来纠正一个很多时候初学者易犯的错误。看看下图,大家一开始的反应估计都是,这还用讲?不就是Init和Finalize是串行,内是并行吗,有什么值得说的?
好,问题来了。
Q:这个串行他是怎么个意义上的串行呢?
A:代码是被copy到所有并行进程的,所以在Init和Finalize外也会被所有的进程全部执行一次。可以试着输出一下my_rank,就可以看出区别了。
那么这告诉我们要小心什么呢?
小心内存咯,不要把一个共享的内存让每个进程都自己开——一——遍——啊——!
好滴,继续继续!我们想要进行进程间通信,没有通信接口,可怎么办啊,这可不行啊
程序猿想找对象,但是你发现没有传递心意的对象,哪找p啊。快来看看怎么传递吧。
什么?你有对象了,那没事了(我什么时候也能有呢嘤嘤嘤)。那就看看怎么传递消息吧。
注意,这里标签的使用也很关键,不可或缺啊!
当然了,写这种并行程序的时候也一定要注意死锁,千万不要出现这种情况,互相等待实在太痛苦了(怎么又好像回到搞对象的话题了)。
那么言归正传,程序要怎么写呢?
首先你可能要定义一系列可能用到的变量,然后开始Init进入到并行区域,获取进程个数和自己的PID,然后开一个矩阵,然后初始化确定规模和初值(前面提到了,在哪里初始化不言而喻了吧),然后将对应信息在进程间传递并让每个进程自己转置自己的部分再把最终信息汇总即可。
#include
#include
#include
#include
#include "mpi.h"
int **matrix, **out, *tmp;//输入输出矩阵,传递时候的临时变量
int n;
double t_start, t_end;
MPI_Status status;
void init(){
matrix = (int **)malloc(n * sizeof(int *));
out = (int **)malloc(n * sizeof(int *));
for(int i=0;i<n;i++){
matrix[i] = (int *)malloc(n * sizeof(int));
out[i] = (int *)malloc(n * sizeof(int));
}
}
void getValue(){
int cnt = 0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
matrix[i][j] = out[i][j] = cnt;
cnt++;
}
}
}
void print(){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d ",out[i][j]);
}
printf("\n");
}
}
int main(int argc, char *argv[]){
//矩阵维数
n = atoi(argv[1]);
int my_rank,group_size;
int sqrt_group_size,u,v,length;//进程数开根号即每行或每列的子块数,列号,行号,每个块的维数
//init之前不是简单的想象中并行,这里对于每个进程而言都是串行执行了一下
//开始并行程序
MPI_Init(&argc, &argv);
//获取进程数
MPI_Comm_size(MPI_COMM_WORLD,&group_size);
//获取PID
MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
sqrt_group_size = sqrt(group_size);
u = my_rank%sqrt_group_size;
v = my_rank/sqrt_group_size;
length = n/sqrt_group_size;
tmp = (int *)malloc(sizeof(int)*length*length);
if(my_rank == 0){
//0号进程对矩阵初始化,不知道为什么放在并行化外初始化会段错误
init();
getValue();
t_start = MPI_Wtime();
//这两层循环是找到对应编号的子块
for(int i=0;i<sqrt_group_size;i++){
for(int j=0;j<sqrt_group_size;j++){
//这里该确定每一个子块中的元素对应在matrix里的位置了
//貌似传递的数据需存在一维数组中,二维可能存在内存不连续的状况
int point = 0;
for(int k=length*i;k<length*(i+1);k++){
for(int l=length*j;l<length*(j+1);l++){
tmp[point] = matrix[k][l];
point++;
}
}
//0号子块由0号进程自己转置
if(i==0 && j==0){
for(int i=0;i<length;i++){
for(int j=0;j<length;j++){
out[i][j] = matrix[j][i];
}
}
}
//其他子块送到其他进程中去转置
else{
MPI_Send(tmp,length*length,MPI_INT,i*sqrt_group_size+j,i*sqrt_group_size+j,MPI_COMM_WORLD);
}
}
}
}
else{
//printf("myrank=%d\n",my_rank);
MPI_Recv(tmp,length*length,MPI_INT,0,my_rank,MPI_COMM_WORLD,&status);
//printf("myrank %d receive 0 successfully\n",my_rank);
//内部转置
int t;
for(int i=0;i<length;i++){
for(int j=i+1;j<length;j++){
t = tmp[i*length+j];
tmp[i*length+j] = tmp[j*length+i];
tmp[j*length+i] = t;
}
}
MPI_Send(tmp,length*length,MPI_INT,0,my_rank,MPI_COMM_WORLD);
}
if(my_rank==0){
//还是先找每一个子块对应的进程
for(int i=0;i<sqrt_group_size;i++){
for(int j=0;j<sqrt_group_size;j++){
if(i!=0 || j!=0){
//printf("0 receive myrank=%d\n",i*sqrt_group_size+j);
MPI_Recv(tmp,length*length,MPI_INT,i*sqrt_group_size+j,i*sqrt_group_size+j,MPI_COMM_WORLD,&status);
//printf("0 receive myrank=%d successfully\n",i*sqrt_group_size+j);
for(int x=0;x<length;x++){
for(int y=0;y<length;y++){
//要交换的子块进行交换
out[j*length+x][i*length+y] = tmp[x*length+y];
}
}
}
}
}
t_end = MPI_Wtime();
printf("Matrix order:%d, Time cost:%lf\n",n,t_end-t_start);
print();
free(matrix);
}
free(tmp);
MPI_Finalize();
return 0;
}
结果如下,
0 10 20 30 40 50 60 70 80 90
1 11 21 31 41 51 61 71 81 91
2 12 22 32 42 52 62 72 82 92
3 13 23 33 43 53 63 73 83 93
4 14 24 34 44 54 64 74 84 94
5 15 25 35 45 55 65 75 85 95
6 16 26 36 46 56 66 76 86 96
7 17 27 37 47 57 67 77 87 97
8 18 28 38 48 58 68 78 88 98
9 19 29 39 49 59 69 79 89 99
好了,到这里这篇文章就结束了,希望大家能有所收获,也希望能指出我的不足,共同进步。