好了這次叫我們來看看什麼是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 <stdio.h>
#include <malloc.h>
#include <cmath>
#include <cstdlib>
#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
好了,到這裏這篇文章就結束了,希望大家能有所收穫,也希望能指出我的不足,共同進步。