MPI矩陣乘法的兩種實現方法

MPI矩陣乘法

去年學習了並行計算,接觸了MPI、Pthreads和OpenMP等常用的並行方法實現了並行的矩陣乘法,本章在此總結一下MPI的矩陣乘法使用。

  • 使用簡單的MPI_Send和MPI_Recv實現
  • 使用較高級的MPI_Scatter和MPI_Gather實現

MPI_Send和MPI_Recv實現

#include<stdio.h>
#include<stdlib.h>
#include<mpi.h>
#include<time.h>

int main(int argc,char *argv[])
{
    double start, stop;
    int i, j, k, l;
    int *a, *b, *c, *buffer, *ans;
    int size = 1000;
    int rank, numprocs, line;

    MPI_Init(NULL,NULL);//MPI Initialize
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);//獲得當前進程號
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);//獲得進程個數

    line = size/numprocs;//將數據分爲(進程數)個塊,主進程也要處理數據
    a = (int*)malloc(sizeof(int)*size*size);
    b = (int*)malloc(sizeof(int)*size*size);
    c = (int*)malloc(sizeof(int)*size*size);
    //緩存大小大於等於要處理的數據大小,大於時只需關注實際數據那部分
    buffer = (int*)malloc(sizeof(int)*size*line);//數據分組大小
    ans = (int*)malloc(sizeof(int)*size*line);//保存數據塊計算的結果

    //主進程對矩陣賦初值,並將矩陣N廣播到各進程,將矩陣M分組廣播到各進程
    if (rank==0)
    {
        //從文件中讀入矩陣
        FILE *fp;

        fp=fopen("a.txt","r");//打開文件
        start = MPI_Wtime();
        for(i=0;i<1000;i++) //讀數據
            for(j=0;j<1000;j++)
                fscanf(fp,"%d",&a[i*size+j]);  
        fclose(fp);//關閉文件

        fp=fopen("b.txt","r");

        for(i=0;i<1000;i++) 
            for(j=0;j<1000;j++)
                fscanf(fp,"%d",&b[i*size+j]);  
        fclose(fp);
        //將矩陣N發送給其他從進程
        for (i=1;i<numprocs;i++)
        {
                MPI_Send(b,size*size,MPI_INT,i,0,MPI_COMM_WORLD);
        }
        //依次將a的各行發送給各從進程
        for (l=1; l<numprocs; l++)
        {
            MPI_Send(a+(l-1)*line*size,size*line,MPI_INT,l,1,MPI_COMM_WORLD);
        }
        //接收從進程計算的結果
        for (k=1;k<numprocs;k++)
        {
            MPI_Recv(ans,line*size,MPI_INT,k,3,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
            //將結果傳遞給數組c
            for (i=0;i<line;i++)
            {
                for (j=0;j<size;j++)
                {
                    c[((k-1)*line+i)*size+j] = ans[i*size+j];
                }

            }
        }
        //計算a剩下的數據
        for (i=(numprocs-1)*line;i<size;i++)
        {
            for (j=0;j<size;j++)
            {
                int temp=0;
                for (k=0;k<size;k++)
                    temp += a[i*size+k]*b[k*size+j];
                c[i*size+j] = temp;
            }
        }

        fp=fopen("c.txt","w");
        for(i=0; i<size; i++){
            for(j=0; j<size; j++)
                fprintf(fp,"%d ",c[i*size+j]);
            fputc('\n',fp);
        }
        fclose(fp);
        //結果測試
        //統計時間
        stop = MPI_Wtime();

        printf("rank:%d time:%lfs\n",rank,stop-start); 

        free(a);
        free(b);
        free(c);
        free(buffer);
        free(ans);
    }

    //其他進程接收數據,計算結果後,發送給主進程
    else
    {
        //接收廣播的數據(矩陣b)
        MPI_Recv(b,size*size,MPI_INT,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

        MPI_Recv(buffer,size*line,MPI_INT,0,1,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
        //計算乘積結果,並將結果發送給主進程
        for (i=0;i<line;i++)
        {
            for (j=0;j<size;j++)
            {
                int temp=0;
                for(k=0;k<size;k++)
                    temp += buffer[i*size+k]*b[k*size+j];
                ans[i*size+j]=temp;
            }
        }
        //將計算結果傳送給主進程
        MPI_Send(ans,line*size,MPI_INT,0,3,MPI_COMM_WORLD);
    }

    MPI_Finalize();//結束

    return 0;
}

MPI_Scatter和MPI_Gather實現

#include<stdio.h>
#include<mpi.h>
#include <malloc.h>
#define M 1000
#define N 1000
int main()
{
int my_rank;/*My process rank*/
int comm_sz;/*Number of processes*/
int local_M;
int i,j,k;
double start,finish;/*timer*/
int tem;
//初始化MPI
MPI_Init(NULL,NULL);
MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
//每個矩陣分配到的行數
local_M=M/comm_sz;
//分配到每個進程的矩陣
int *local_Matrix_one=(int*)malloc(local_M*N*sizeof(int));
//定義兩個矩陣
int *Matrix_one=NULL;
int *Matrix_two=(int*)malloc(M*N*sizeof(int));
//每個進程裏的結果矩陣
int *local_result=(int*)malloc(local_M*N*sizeof(int));
//結果矩陣
int *result_Matrix=NULL;
if(my_rank==0)
{
//printf("process %d of %d\n",my_rank,comm_sz);
FILE * fp;
//讀取第一個矩陣
Matrix_one=(int*)malloc(M*N*sizeof(int));
//Matrix_one[M][N]={0};
fp=fopen("a.txt","r");
for(i=0;i<M;i++)
{
for(j=0;j<N;j++)
fscanf(fp,"%d ",&Matrix_one[i*N+j]);
fscanf(fp,"\n");
}
fclose(fp);
/*for(i=0;i<M;i++)
{
for(j=0;j<N;j++)
   printf("%d ",Matrix_one[i*N+j]);
   printf("\n");
}*/
//讀取第二個矩陣
start=MPI_Wtime();
fp=fopen("b.txt","r");
for(j=0;j<N;j++)
{
for(i=0;i<M;i++)
fscanf(fp,"%d ",&Matrix_two[i*N+j]);
fscanf(fp,"\n");
}
fclose(fp);
/*for(j=0;j<N;j++)
{
for(i=0;i<M;i++)
   printf("%d ",Matrix_two[i*M+j]);
   printf("\n");
}*/
//數據分發
MPI_Scatter(Matrix_one,local_M*N,MPI_INT,local_Matrix_one,local_M*N,MPI_INT,0,MPI_COMM_WORLD);

//數據廣播
MPI_Bcast(Matrix_two,M*N,MPI_INT,0,MPI_COMM_WORLD);
//計算local結果
for(i=0;i<local_M;i++)
for(j=0;j<M;j++){
tem=0;
for(k=0;k<N;k++)
tem +=local_Matrix_one[i*M+k]*Matrix_two[j*M+k];
local_result[i*M+j]=tem;
}
free(local_Matrix_one);
result_Matrix=(int*)malloc(M*N*sizeof(int));
//結果聚集
MPI_Gather(local_result,local_M*N,MPI_INT,result_Matrix,local_M*N,MPI_INT,0,MPI_COMM_WORLD);
//剩餘行處理(處理不能整除的情況)
int rest=M%comm_sz;
if(rest!=0)
for(i=M-rest-1;i<M;i++)
for(j=0;j<M;j++){
tem=0;
for(k=0;k<N;k++)
tem +=Matrix_one[i*M+k]*Matrix_two[j*M+k];
result_Matrix[i*M+j]=tem;
}
finish=MPI_Wtime();
free(Matrix_one);
free(Matrix_two);
free(local_result);

printf("Proc %d > Elapsed time = %e seconds\n",my_rank,finish-start);
//將結果寫入文件
fp=fopen("c.txt","w");
for(i=0;i<M;i++)
{
for(j=0;j<N;j++)
fprintf(fp,"%d ",result_Matrix[i*N+j]);
fscanf(fp,"\n");
}
fclose(fp);
/*for(i=0;i<local_M;i++)
{
for(j=0;j<N;j++)
   printf("%d ",local_result[i*N+j]);
   printf("\n");
}*/
}
else{
//printf("process %d of %d\n",my_rank,comm_sz);
//數據分發
MPI_Scatter(Matrix_one,local_M*N,MPI_INT,local_Matrix_one,local_M*N,MPI_INT,0,MPI_COMM_WORLD);
//數據廣播
MPI_Bcast(Matrix_two,M*N,MPI_INT,0,MPI_COMM_WORLD);
//計算local結果
for(i=0;i<local_M;i++)
for(j=0;j<M;j++){
tem=0;
for(k=0;k<N;k++)
tem +=local_Matrix_one[i*M+k]*Matrix_two[j*M+k];
local_result[i*M+j]=tem;
}
free(local_Matrix_one);
free(Matrix_two);
//結果聚集
MPI_Gather(local_result,local_M*N,MPI_INT,result_Matrix,local_M*N,MPI_INT,0,MPI_COMM_WORLD);
free(local_result);
//printf("%d %d\n",local_M,my_rank);
}
MPI_Finalize();
return 0;
}

結果分析


① 執行時間分析:
並行時,隨着進程數目的增多,並行計算的時間越來越短;當達到一定的進程數時,執行時間小到最小值;然後再隨着進程數的增多,執行時間反而越來越長。
② 加速比分析:
隨着進程數的增大,加速比也是逐漸增大到最大值;再隨着進程數的增大,加速比逐漸減小。
③ 執行效率分析:
隨着進程數的增大,程序執行效率不斷降低
④ 原因分析:
MPI並行程序的測試平臺爲Intel Core i5 CPU,爲雙核CPU,即在一個處理器上集成兩個運算核心,提高了運算效率,因此會比串行的執行時間要短。由於一個進程只能在一個核上執行,因此只能有兩個進程並行執行,又因爲多進程運行在兩個CPU上,會有進程切換等操作,所以纔會出現進程數增加而執行時間增加的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章