当前位置:文档之家› 并行计算矩阵分块乘法

并行计算矩阵分块乘法

目录一、题目及要求 (1)1、题目 (1)2、要求 (1)二、设计算法、算法原理 (1)三、算法描述、设计流程 (2)3.1算法描述 (2)3.2设计流程 (4)四、源程序代码及运行结果 (6)1、超立方 (6)1.1超立方的源程序代码 (6)1.2运行结果 (11)2、网孔连接 (11)2.1源程序代码 (11)2.2运行结果 (18)3、在数学软件中的计算结果 (19)五、算法分析、优缺点 (19)1、简单的并行分块乘法的过程为 (19)2、使用Cannon算法时的算法分析 (20)3、算法的优缺点 (21)六、总结 (22)参考文献 (23)一、题目及要求1、题目简单并行分块乘法:(1)情形1: 超立方连接;(2)情形2:二维环绕网孔连接已知,177511195310135411274329,75638957123142120143321⎪⎪⎪⎪⎪⎭⎫ ⎝⎛----=⎪⎪⎪⎪⎪⎭⎫⎝⎛----=B A 求B A C ⨯=。

2、要求(1)题目分析、查阅与题目相关的资料; (2)设计算法;(3)算法实现、代码编写; (4)结果分析和性能分析与改进; (5)论文撰写、答辩;二、设计算法、算法原理要考虑的计算问题是C=AB,其中A 与B 分别是n n ⨯矩阵。

①A 、B 和C 分成p p p ⨯=的方块阵ij A ,ij B 和ij C ,大小均为pnp n ⨯,p 个处理器编号为1,1, (1)0,....,0,0---p p p pp p , ij P 存放ij A ,ij B 和ij C 。

②通讯:每行处理器进行A 矩阵块的多到多播送(得到ik A , k=0~1-p ) 每列处理器进行B 矩阵块的多到多播送(得到kj B , k=0~ 1-p )③乘-加运算: ij P 做kj p k ikij B AC ∑-==1三、算法描述、设计流程3.1算法描述超立方情形下矩阵的简单并行分块算法 输入:待选路的信包在源处理器中 输出:将原处理器中的信包送至其目的地 Begin(1) for i=1 to n do11--⊗=i i i d s r endfor(2) S V i ==,1 (3) while n i ≤do(3.1)if 1=i r then 从当前节点V 选路到节点为V ⊗1 (3.2)1+=i i endwhile End二维网孔情形下矩阵的简单并行分块算法 输入:待选路的信包处于源处理器中 输出:将各信包送至各自的目的地中 Begin(1) 沿x 维将信包向左或向右选路至目的地的处理器所在的列 (2) 沿y 维将信包向上或向下选路至目的地的处理器所在的行 分块乘法算法//输入: n n A ⨯,n n B ⨯ ; 子快大小均为pn pn ⨯输出: n n C ⨯nBegin(1)for i=0 to 1-p do for all par-do ij p if i>k then ij A ←()mod ,1j i A +endifif j>k thenij B ← B (i+1)mod , j endif endfor endforfor i=0 to 1-p do for all ij p par-do ij C =ij A +ij B endfor Endfor End3.2设计流程以下是二维网孔与超立方连接设计流程。

如图3-1 二维网孔 步骤:(1)先进行行播送; (2)再同时进行列播送;图3-1 二维网孔示意图44 3超立方步骤:依次从低维到高维播送, d-立方, d=0,1,2,3,4…; 算法流程如图所示:图3-2 算法流程四、源程序代码及运行结果1、超立方1.1超立方的源程序代码#include "stdio.h"#include "stdlib.h"#include "mpi.h"#define intsize sizeof(int)#define floatsize sizeof(float)#define charsize sizeof(char)#define A(x,y) A[x*K+y]#define B(x,y) B[x*N+y]#define C(x,y) C[x*N+y]#define a(x,y) a[x*K+y]#define b(x,y) b[x*n+y]#define buffer(x,y) buffer[x*n+y]#define c(l,x,y) c[x*N+y+l*n]float *a,*b,*c,*buffer;int s;float *A,*B,*C;int M,N,K,P ;int m,n;int myid;int p;FILE *dataFile;MPI_Status status;double time1;double starttime,endtime;void readData(){int i,j;starttime = MPI_Wtime();dataFile=fopen("yin.txt","r");fscanf(dataFile,"%d%d", &M, &K); A=(float *)malloc(floatsize*M*K); for(i = 0; i < M; i++) {for(j = 0; j < K; j++){fscanf(dataFile,"%f", A+i*K+j);}}fscanf(dataFile,"%d%d", &P, &N); if (K!=P) {printf("the input is wrong\n");exit(1);}B=(float *)malloc(floatsize*K*N); for(i = 0; i < K; i++) {for(j = 0; j < N; j++){fscanf(dataFile,"%f", B+i*N+j);}}fclose(dataFile);printf("Input of file \"yin.txt\"\n");printf("%d\t %d\n",M, K); for(i=0;i<M;i++) {for(j=0;j<K;j++) printf("%f\t",A(i,j));printf("\n");}printf("%d\t %d\n",K, N); for(i=0;i<K;i++) {for(j=0;j<N;j++) printf("%f\t",B(i,j));printf("\n");}C=(float *)malloc(floatsize*M*N); }int gcd(int M,int N,int group_size){int i;for(i=M; i>0; i--){if((M%i==0)&&(N%i==0)&&(i<=group_size))return i;}return 1;}void printResult(){int i,j;printf("\nOutput of Matrix C = AB\n");for(i=0;i<M;i++){for(j=0;j<N;j++) printf("%f\t",C(i,j));printf("\n");}endtime=MPI_Wtime();printf("\n");printf("Whole running time = %f seconds\n",endtime-starttime); printf("Distribute data time = %f seconds\n",time1-starttime); printf("Parallel compute time = %f seconds\n",endtime-time1);}int main(int argc, char **argv){int i,j,k,l,group_size,mp1,mm1;MPI_Init(&argc,&argv);MPI_Comm_size(MPI_COMM_WORLD,&group_size);MPI_Comm_rank(MPI_COMM_WORLD,&myid);p=group_size;if(myid==0){readData();}if (myid==0)for(i=1;i<p;i++){MPI_Send(&M,1,MPI_INT,i,i,MPI_COMM_WORLD);MPI_Send(&K,1,MPI_INT,i,i,MPI_COMM_WORLD);MPI_Send(&N,1,MPI_INT,i,i,MPI_COMM_WORLD);}else{MPI_Recv(&M,1,MPI_INT,0,myid,MPI_COMM_WORLD,&status);MPI_Recv(&K,1,MPI_INT,0,myid,MPI_COMM_WORLD,&status);MPI_Recv(&N,1,MPI_INT,0,myid,MPI_COMM_WORLD,&status);}p=gcd(M,N,group_size);m=M/p;n=N/p;if(myid<p){a=(float *)malloc(floatsize*m*K);b=(float *)malloc(floatsize*K*n);c=(float *)malloc(floatsize*m*N);if (myid%2!=0)buffer=(float *)malloc(K*n*floatsize);if (a==NULL||b==NULL||c==NULL)printf("Allocate space for a,b or c fail!");if (myid==0){for (i=0;i<m;i++)for (j=0;j<K;j++)a(i,j)=A(i,j);for (i=0;i<K;i++)for (j=0;j<n;j++)b(i,j)=B(i,j);}if (myid==0){for (i=1;i<p;i++){MPI_Send(&A(m*i,0),K*m,MPI_FLOAT,i,i,MPI_COMM_WORLD); for (j=0;j<K;j++)MPI_Send(&B(j,n*i),n,MPI_FLOAT,i,i,MPI_COMM_WORLD);}free(A);free(B);}else{MPI_Recv(a,K*m,MPI_FLOAT,0,myid,MPI_COMM_WORLD,&status); for (j=0;j<K;j++)MPI_Recv(&b(j,0),n,MPI_FLOAT,0,myid,MPI_COMM_WORLD,&status);}if (myid==0)time1=MPI_Wtime();for (i=0;i<p;i++){l=(i+myid)%p;for (k=0;k<m;k++)for (j=0;j<n;j++)for (c(l,k,j)=0,s=0;s<K;s++)c(l,k,j)+=a(k,s)*b(s,j);mm1=(p+myid-1)%p;mp1=(myid+1)%p;if (i!=p-1){if(myid%2==0){MPI_Send(b,K*n,MPI_FLOAT,mm1,mm1,MPI_COMM_WORLD);MPI_Recv(b,K*n,MPI_FLOAT,mp1,myid,MPI_COMM_WORLD,&status);}else{for(k=0;k<K;k++)for(j=0;j<n;j++)buffer(k,j)=b(k,j);MPI_Recv(b,K*n,MPI_FLOAT,mp1,myid,MPI_COMM_WORLD,&status);MPI_Send(buffer,K*n,MPI_FLOAT,mm1,mm1,MPI_COMM_WORLD);}}}if (myid==0)for(i=0;i<m;i++)for(j=0;j<N;j++)C(i,j)=*(c+i*N+j);if (myid!=0)MPI_Send(c,m*N,MPI_FLOAT,0,myid,MPI_COMM_WORLD);else{for(k=1;k<p;k++){MPI_Recv(c,m*N,MPI_FLOAT,k,k,MPI_COMM_WORLD,&status); for(i=0;i<m;i++)for(j=0;j<N;j++)C((k*m+i),j)=*(c+i*N+j);}}if(myid==0)printResult();}MPI_Finalize();if(myid<p){free(a);free(b);free(c);if(myid==0)free(C);if(myid%2!=0)free(buffer);}return (0);}1.2运行结果图4.1 4个处理器的运行结果2、网孔连接2.1源程序代码#include <stdlib.h>#include <string.h>#include <mpi.h>#include <time.h>#include <stdio.h>#include <math.h>/* 全局变量声明 */float **A, **B, **C; /* 总矩阵,C = A * B */float *a, *b, *c, *tmp_a, *tmp_b; /* a、b、c表分块,tmp_a、tmp_b表缓冲区 */int dg, dl, dl2,p, sp; /* dg:总矩阵维数;dl:矩阵块维数;dl2=dl*dl;p:处理器个数;sp=sqrt(p) */int my_rank, my_row, my_col; /* my_rank:处理器ID;(my_row,my_col):处理器逻辑阵列坐标 */MPI_Status status;/**函数名: get_index*功能:处理器逻辑阵列坐标至rank号的转换*输入:坐标、逻辑阵列维数*输出:rank号*/int get_index(int row, int col, int sp){return ((row+sp)%sp)*sp + (col+sp)%sp;}/**函数名:random_A_B*功能:随机生成矩阵A和B*/void random_A_B(){int i,j;float m;//srand((unsigned int)time(NULL)); /*设随机数种子*/*随机生成A,B,并初始化C*/for(i=0; i<dg ; i++)for(j=0; j<dg ; j++){scanf("%f",&m);A[i][j] = m;C[i][j] = 0.0;m=0;}for(i=0; i<dg ; i++)for(j=0; j<dg ; j++){scanf("%f",&m);B[i][j] = m;m=0;}}/* 函数名:scatter_A_B* 功能:rank为0的处理器向其他处理器发送A、B矩阵的相关块*/void scatter_A_B(){int i,j,k,l;int p_imin,p_imax,p_jmin,p_jmax;for(k=0; k<p; k++){/*计算相应处理器所分得的矩阵块在总矩阵中的坐标范围*/p_jmin = (k % sp ) * dl;p_jmax = (k % sp + 1) * dl-1;p_imin = (k - (k % sp))/sp * dl;p_imax = ((k - (k % sp))/sp +1) *dl -1;l = 0;/*rank=0的处理器将A,B中的相应块拷至tmp_a,tmp_b,准备向其他处理器发送*/for(i=p_imin; i<=p_imax; i++){for(j=p_jmin; j<=p_jmax; j++){tmp_a[l] = A[i][j];tmp_b[l] = B[i][j];l++;}}/*rank=0的处理器直接将自己对应的矩阵块从tmp_a,tmp_b拷至a,b*/ if(k==0){memcpy(a, tmp_a, dl2 * sizeof(float));memcpy(b, tmp_b, dl2 * sizeof(float));} else /*rank=0的处理器向其他处理器发送tmp_a,tmp_b中相关的矩阵块*/{MPI_Send(tmp_a, dl2, MPI_FLOAT, k, 1, MPI_COMM_WORLD);MPI_Send(tmp_b, dl2, MPI_FLOAT, k, 2, MPI_COMM_WORLD);}}}/**函数名:init_alignment*功能:矩阵A和B初始对准*/void init_alignment(){MPI_Sendrecv(a, dl2, MPI_FLOAT, get_index(my_row,my_col-my_row,sp), 1,tmp_a, dl2, MPI_FLOAT, get_index(my_row,my_col+my_row,sp), 1, MPI_COMM_WORLD, &status);memcpy(a, tmp_a, dl2 * sizeof(float) );/*将B中坐标为(i,j)的分块B(i,j)向上循环移动j步*/MPI_Sendrecv(b, dl2, MPI_FLOAT, get_index(my_row-my_col,my_col,sp), 1,tmp_b, dl2, MPI_FLOAT, get_index(my_row+my_col,my_col,sp), 1, MPI_COMM_WORLD, &status);memcpy(b, tmp_b, dl2 * sizeof(float) );}/**函数名:main_shift*功能:分块矩阵左移和上移,并计算分块c*/void main_shift(){int i,j,k,l;for(l=0; l<sp; l++){/*矩阵块相乘,c+=a*b */for(i=0; i<dl; i++)for(j=0; j<dl; j++)for(k=0; k<dl; k++)c[i*dl+j] += a[i*dl+k]*b[k*dl+j];/* 将分块a左移1位 */MPI_Send(a , dl2, MPI_FLOAT, get_index(my_row, my_col-1, sp), 1, MPI_COMM_WORLD);MPI_Recv(a , dl2, MPI_FLOAT, get_index(my_row, my_col+1, sp), 1, MPI_COMM_WORLD, &status);/* 将分块b上移1位 */MPI_Send(b , dl2, MPI_FLOAT, get_index(my_row-1, my_col, sp), 1, MPI_COMM_WORLD);MPI_Recv(b , dl2, MPI_FLOAT, get_index(my_row+1, my_col, sp), 1, MPI_COMM_WORLD, &status);}}/**函数名:collect_c*功能:rank为0的处理器从其余处理器收集分块矩阵c*/void collect_C(){int i,j,i2,j2,k;int p_imin,p_imax,p_jmin,p_jmax; /* 分块矩阵在总矩阵中顶点边界值 *//* 将rank为0的处理器中分块矩阵c结果赋给总矩阵C对应位置 */for (i=0;i<dl;i++)for(j=0;j<dl;j++)C[i][j]=c[i*dl+j];for (k=1;k<p;k++){/*将rank为0的处理器从其他处理器接收相应的分块c*/MPI_Recv(c, dl2, MPI_FLOAT, k, 1, MPI_COMM_WORLD, &status);p_jmin = (k % sp ) *dl;p_jmax = (k % sp + 1) *dl-1;p_imin = (k - (k % sp))/sp *dl;p_imax = ((k - (k % sp))/sp +1) *dl -1;i2=0;/*将接收到的c拷至C中的相应位置,从而构造出C*/for(i=p_imin; i<=p_imax; i++){j2=0;for(j=p_jmin; j<=p_jmax; j++){C[i][j]=c[i2*dl+j2];j2++;}i2++;}}}/*函数名:print*功能:打印矩阵*输入:指向矩阵指针的指针,字符串*/void print(float **m,char *str){int i,j;printf("%s",str);/*打印矩阵m*/for(i=0;i<dg;i++){for(j=0;j<dg;j++)printf("%15.0f ",m[i][j]);printf("\n");}printf("\n");}/**函数名:main*功能:主过程,Cannon算法,矩阵相乘*输入:argc为命令行参数个数,argv为每个命令行参数组成的字符串数组 */int main(int argc, char *argv[]){int i;MPI_Init(&argc, &argv); /* 启动MPI计算 */MPI_Comm_size(MPI_COMM_WORLD, &p); /* 确定处理器个数 */MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); /* 确定各自的处理器标识符*/sp = sqrt(p);/* 确保处理器个数是完全平方数,否则打印错误信息,程序退出 */if (sp*sp != p){if (my_rank == 0)printf("Number of processors is not a quadratic number!\n");MPI_Finalize();exit(1);}if (argc != 2){if (my_rank == 0)printf("usage: mpirun -np ProcNum cannon MatrixDimension\n"); MPI_Finalize();exit(1);}dg = atoi(argv[1]); /* 总矩阵维数 */dl = dg / sp; /* 计算分块矩阵维数 */dl2 = dl * dl;/* 计算处理器在逻辑阵列中的坐标 */my_col = my_rank % sp ;my_row = (my_rank-my_col) / sp ;/* 为a、b、c分配空间 */a = (float *)malloc( dl2 * sizeof(float) );b = (float *)malloc( dl2 * sizeof(float) );c = (float *)malloc( dl2 * sizeof(float) );/* 初始化c */for(i=0; i<dl2 ; i++)c[i] = 0.0;/* 为tmp_a、tmp_b分配空间 */tmp_a = (float *)malloc( dl2 * sizeof(float) );tmp_b = (float *)malloc( dl2 * sizeof(float) );if (my_rank == 0){/* rank为0的处理器为A、B、C分配空间 */A = (float **)malloc( dg * sizeof(float*) );B = (float **)malloc( dg * sizeof(float*) );C = (float **)malloc( dg * sizeof(float*) );for(i=0; i<dg; i++){A[i] = (float *)malloc( dg * sizeof(float) );B[i] = (float *)malloc( dg * sizeof(float) );C[i] = (float *)malloc( dg * sizeof(float) );}random_A_B(); /* rank为0的处理器随机化生成A、B矩阵 */scatter_A_B(); /* rank为0的处理器向其他处理器发送A、B矩阵的相关块 */}else /* rank不为0的处理器接收来自rank为0的处理器的相应矩阵分块 */{MPI_Recv(a, dl2, MPI_FLOAT, 0 , 1, MPI_COMM_WORLD, &status); MPI_Recv(b, dl2, MPI_FLOAT, 0 , 2, MPI_COMM_WORLD, &status);}init_alignment(); /* A、B矩阵的初始对准 */main_shift(); /* 分块矩阵左移、上移, cannon算法的主过程 */if(my_rank == 0){collect_C(); /* rank为0的处理器从其余处理器收集分块矩阵c */print(A,"random matrix A : \n"); /* 打印矩阵A */print(B,"random matrix B : \n"); /* 打印矩阵B */print(C,"Matrix C = A * B : \n"); /* 打印矩阵C */} else{MPI_Send(c,dl2,MPI_FLOAT,0,1,MPI_COMM_WORLD); }MPI_Barrier(MPI_COMM_WORLD); /* 同步所有处理器 */MPI_Finalize(); /* 结束MPI计算 */return 0;}2.2运行结果图4.2 4个处理器的运行结果3、在数学软件中的计算结果图4.3 在MATLAB 中的运行结果五、算法分析、优缺点1、简单的并行分块乘法的过程为(1)分块:将: A n ×n 与 B n ×n 分成p 块A i,j 和B i,j (0≤i,j ≤1-p ),每块大小为)/()/(p n p n ⨯,并将它们分配给p p ⨯个处理器 (1,11,00,0,...,,...,---p p p P P P )。

相关主题