并行计算矩阵分块乘法
分块矩阵求矩阵乘法算法

分块矩阵求矩阵乘法算法矩阵乘法是线性代数中的一个重要概念,在计算机科学和数学领域有广泛的应用。
而分块矩阵求矩阵乘法算法则是一种优化的方法,能够提高矩阵乘法的效率。
本文将介绍分块矩阵求矩阵乘法算法的原理和应用。
1. 算法原理分块矩阵求矩阵乘法算法的核心思想是将待计算的矩阵划分成多个小矩阵,然后利用小矩阵之间的乘法性质进行计算。
具体步骤如下:1.1 将矩阵A和矩阵B划分成多个大小相等的子矩阵,分别记为A11、A12、A21、A22和B11、B12、B21、B22。
1.2 根据矩阵乘法的定义,我们可以得到以下等式:C11 = A11 * B11 + A12 * B21C12 = A11 * B12 + A12 * B22C21 = A21 * B11 + A22 * B21C22 = A21 * B12 + A22 * B221.3 分别计算C11、C12、C21和C22,然后将它们组合成最终的结果矩阵C。
2. 算法优势分块矩阵求矩阵乘法算法相对于传统的矩阵乘法算法具有以下优势:2.1 减少计算量:通过将矩阵划分成多个小矩阵,可以减少乘法和加法的次数,从而减少计算量。
2.2 提高并行性:由于小矩阵之间的乘法是独立进行的,可以利用并行计算的优势,提高计算效率。
2.3 提高缓存命中率:分块矩阵乘法算法可以使得计算时所需的数据更加紧凑地存储在连续的内存中,从而提高缓存的命中率,减少数据的访存时间。
3. 算法应用分块矩阵求矩阵乘法算法在科学计算和工程领域有广泛的应用,特别是在大规模矩阵乘法计算和并行计算中更加突出其优势。
3.1 大规模矩阵乘法:对于大规模的矩阵乘法计算,传统的方法往往会面临计算量大、计算时间长的问题。
而分块矩阵求矩阵乘法算法可以将大规模的矩阵划分成多个小矩阵,从而减少计算量,提高计算效率。
3.2 并行计算:分块矩阵求矩阵乘法算法的并行性很好,可以通过将不同的小矩阵分配给不同的计算单元进行并行计算,从而大大提高计算效率。
矩阵相乘法则

矩阵相乘法则矩阵相乘法则是线性代数中的重要内容。
它描述了如何将两个矩阵相乘,并且提供了一些非常有用的解决问题的方法。
在本文中,我们将介绍矩阵相乘法则的各个方面。
1. 矩阵的乘法矩阵的乘法是线性代数中一个基本概念。
如果有两个矩阵$A$和$B$,它们可以相乘当且仅当第一个矩阵的列数等于第二个矩阵的行数。
如果$A$是$m×n$的矩阵,$B$是$n×p$的矩阵,那么它们的乘积为 $C=AB$,结果矩阵$C$是$m×p$的矩阵。
在矩阵$C$中,元素$c_{ij}$的值是矩阵$A$的第$i$行和矩阵$B$的第$j$列的乘积之和,即:$${\displaystyle c_{ij}=\sum_{k=1}^{n}a_{ik}b_{kj}}$$以下是矩阵乘法的一个例子:$${\displaystyle \begin{pmatrix}1 & 2 & 3\\4 & 5 & 6\end{pmatrix}\begin{pmatrix}7 & 8\\9 & 10\\11 & 12\end{pmatrix}=\begin{pmatrix}58 & 64\\139 & 154\end{pmatrix}}$$2. 矩阵相乘的性质矩阵相乘具有以下性质:(1)结合律:$(AB)C=A(BC)$(2)分配律:$A(B+C)=AB+AC$;$(A+B)C=AC+BC$(3)不满足交换律:$AB\neq BA$。
可以看到,矩阵相乘的结合律和分配律与实数的运算性质相似。
但是,矩阵相乘不满足交换律,即矩阵的乘积与乘法的顺序有关。
这是因为在矩阵相乘时,乘法的顺序会影响结果矩阵中元素的计算方式。
3. 矩阵乘法的应用矩阵相乘法则不仅仅是线性代数的基本内容,还被广泛应用于其他领域,如计算机科学、物理学、经济学、统计学等。
以下是一些矩阵相乘的应用:(1)图像处理图像可以表示为像素矩阵,矩阵相乘可以实现图像的旋转、缩放等变换。
矩阵乘法并行算法分析

4. 实验
• 程序说明 • 程序中通过继承Runable接口来实现,原因 在于Java类只能单继承,如果采用第一种方 法,即继承了Thread类后,就不能再继承其 他的类了,使得程序丧失了灵活性。而通 过实现Runnable接口的方法,可以很好的解 决这个问题。
4. 实验
• 程序实现 • 构造了一个conMatrix 的矩阵类用于初始化 矩阵(矩阵用二维数组表示)。 • 构造了继承自conMatrix的Matrix类,并在类 中实现Runable接口。 • 通过矩阵相乘方法“chengfa(…,int n)”中的 第3个参数n,来决定将这两个矩阵分成多 少个子块进行计算。并通过获得运算前后 系统时间来得到运算的时间,显示在运算 结果后。
a0,0 a1,0
a0,1 a0,2 a1,1 a1,2 a2,1 a2,2 a3,1 a3,2
a0,3 b0,0 a1,3 b1,0 a2,3 b2,0 a3,3 b3,0
b0,1 b0,2 b1,1 b1,2 b2,1 b2,2 b3,1 b3,2
3. 块矩阵乘法中常用算法分析
• 行列划分算法 由于使用p个处理机, 每次每台处理机计算 出一个Ci,j,计算C需要p次来完成。Ci,j的计 算是按对角线进行的,计算方法如下:
for (i=0; i<p-1; i++){ l=i+myid mod p; Cl=A*B; mp1=myid+1 mod p;mm1=myid-1 mod p; if (i!=p-1) { send(B,mm1);recv(B,mp1);} }
a0,3 b2,0 a1,3 b3,0
b0,3 b1,3 b2,3 b3,3
并行算法与计算数学

并行算法与计算数学随着计算机性能的提高,数据规模的增大,串行算法已经不能满足人们对计算速度的要求。
因此,人们开始研究并行算法,以提高计算效率。
并行算法是指在多个处理器上同时执行的算法,它能够充分利用计算机的计算资源,提高计算速度。
在计算数学领域,一些计算问题本身就是并行的,如矩阵乘法、图像处理等。
下面,我们将介绍一些常见的并行算法和在计算数学中的应用。
1.并行排序算法排序是计算机科学中常见的问题,排序算法的效率直接影响到计算速度。
在串行算法中,快速排序和归并排序是常用的排序算法。
但是这些算法的时间复杂度均为O(nlog n),无法满足大规模数据的排序需求。
因此,人们开始研究并行排序算法。
并行排序算法可以分为两类,一类是基于比较的排序算法,如奇偶排序、快速排序等;另一类是基于分布式内存的排序算法,如桶排序、基数排序等。
在计算数学领域,排序算法也有着广泛的应用。
例如,在解决最小生成树问题时,需要对边按边权进行排序;在求解线性方程组时,需要对系数矩阵进行排序。
2.并行矩阵乘法算法矩阵乘法是计算数学中一项重要的计算任务,其时间复杂度为O(n^3),对于大规模矩阵乘法问题,串行算法已经无法满足要求。
因此,人们开始研究并行矩阵乘法算法。
常用的并行矩阵乘法算法有分块矩阵乘法、Cannon算法、Fox算法等。
这些算法都是基于矩阵的分块思想,通过将大矩阵分割成小块再进行矩阵乘法,从而充分利用计算机的并行计算能力,提高计算速度。
在计算数学领域,矩阵乘法算法也有广泛的应用。
例如,在求解线性方程组时,需要对系数矩阵进行矩阵乘法;在图像处理中,需要对像素矩阵进行矩阵乘法。
3.并行图像处理算法图像处理是计算数学中的一项重要研究领域,其算法主要包括图像增强、图像恢复、图像分割、图像分类等。
在串行算法中,常用的图像处理算法有灰度变换、直方图均衡化、滤波等。
但是,这些串行算法只能处理小规模的图像。
对于大规模的图像,串行算法的计算速度完全无法满足要求。
npu矩阵乘法分块策略

npu矩阵乘法分块策略NPU矩阵乘法分块策略矩阵乘法是线性代数中常见的基本运算,也是许多科学计算和工程应用中必不可少的运算之一。
在现代计算机体系结构中,为了提高矩阵乘法的计算效率,研究人员提出了许多优化方法,其中一种常见的方法是使用NPU(神经处理单元)进行矩阵乘法的计算。
而矩阵乘法分块策略则是在NPU上进行矩阵乘法计算时的一种重要技术。
矩阵乘法分块策略的思想是将大的矩阵乘法问题拆分成多个小的矩阵乘法问题,并通过合理的计算顺序和数据传输方式来提高计算效率。
具体而言,矩阵乘法分块策略可以分为两个层次:外层循环和内层循环。
外层循环是指对于两个矩阵A和B,将它们分别划分成多个小的子矩阵,并按照一定的顺序对这些子矩阵进行计算。
这种分块方式可以使得计算过程中的数据访问更加连续,减少了缓存的失效,从而提高了计算效率。
同时,外层循环还可以通过并行计算的方式,将计算任务分配给多个NPU进行并行处理,进一步提高了计算速度。
内层循环是指在每个小的子矩阵中,使用传统的矩阵乘法算法进行计算。
在传统的矩阵乘法算法中,我们通常使用三个嵌套的循环来遍历矩阵的元素,并进行相应的乘法和累加操作。
而在NPU中,我们可以利用SIMD(单指令多数据)指令集来进行向量化计算,从而进一步提高计算效率。
通过合理地划分内层循环的计算任务,我们可以充分利用NPU的向量计算能力,加速矩阵乘法的计算过程。
除了外层循环和内层循环,矩阵乘法分块策略还需要考虑数据传输的方式。
在NPU中,数据传输的延迟是影响计算效率的一个重要因素。
因此,我们需要将需要的数据尽可能地从主存或其他存储器中提前加载到NPU的缓存中,以减少数据传输的延迟。
同时,我们还需要合理地安排数据传输的顺序,以避免数据传输的冲突和带宽瓶颈,进一步提高计算效率。
总结起来,NPU矩阵乘法分块策略是一种通过将大的矩阵乘法问题拆分成多个小的子问题,并通过合理的计算顺序和数据传输方式来提高计算效率的方法。
大规模矩阵相乘的并行算法

大规模矩阵相乘的并行算法
作者:朱彦辑国佳佳
来源:《电脑知识与技术》2017年第18期
摘要:在大型的科学计算中,矩阵乘法运算是耗时较多的运算,也是工程数值计算中一种常见的运算方式。
串行计算程序由于计算时间和计算效率不尽人意,已经不能满足人们的需求,为了降低计算所消耗的时间,人们一直在研究合适的可用于并行的计算矩阵相乘的方法,和串行算法相比,矩阵相乘的并行算法要考虑更多方面的问题。
该文通过运用API,OpenMP 多核并行计算,将矩阵按一定规则分块传入每个进程,分别进行矩阵相乘运算,这样可以将计算时间缩短大半。
关键词:矩阵相乘;API多核并行;OpenMP并行
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2017)18-0059-03。
矩阵相乘-并行算法

矩阵相乘-并行算法LT行度。
对于一个n×n的方阵,棋盘划分最多可以使用n^2个处理器进行并行计算,但使用按行或列分解最多可以使用n个。
对矩阵相乘采用棋盘式划分的算法通常称作Cannon算法。
A)行列划分又叫带状划分(Striped Partitioning),就是将矩阵整行或者整列分成若干个组,每个组指派给一个处理器。
下图所例为4个CPU,8×8矩阵的带状划分。
在带状划分情况下,每个CPU将会均匀分配到2行(列)数据。
8×8矩阵变成了一个1×4或4×1的分块矩阵,每个CPU所属的分块矩阵大小为8×2或2×8。
B)棋盘划分就是将矩阵分成若干个子矩阵,每个子矩阵指派给一个处理器,此时任一处理器均不包含整行或者整列。
下图所示即为4个处理器情况下8×8矩阵的棋盘划分,其中处理器阵列为2×2,每个处理器分配到的子矩阵大小为4×4。
矩阵划分成棋盘状可以和处理器连成二维网孔相对应。
对于一个n×n维矩阵和p×p的二维处理器阵列,每个处理器均匀分配有(n/p)×(n/p)=n^2/p^2个元素。
使用棋盘式划分的矩阵相乘算法一般有两种,Cannon算法和Summa算法。
SUMMA算法能够计算m*l的A矩阵和l*n的B矩阵相乘(m、l、n可不相等),而cannon算法只能实现n*n的A矩阵和n*n的B矩阵相乘,具有很大的局限性。
3.2、算法原理A) 行划分法假设是M*N,计算前,将矩阵N发送给所有从进程,然后将矩阵M分块,将M中数据按行分给各从进程,在从进程中计算M中部分行数据和N的乘积,最后将结果发送给主进程。
这里为了方便,有多少进程,就将M分了多少块,除最后一块外的其他数据块大小都相等,最后一块是剩下的数据,大小大于等于其他数据块大小,因为矩阵行数不一定整除进程数。
最后一块数据在主进程中计算,其他的在从进程中计算。
分块乘法的初等变换及应用介绍举例

分块乘法的历史与发展
01
分块乘法的思想起源于19世纪中叶,当时主要用于解决线性方 程组问题。
02
随着计算机技术的发展,分块乘法在数值计算、图像处理、机
器学习等领域得到了广泛应用。
近年来,分块乘法的研究主要集中在算法优化、并行计算等方
03
面,以提高计算速度和效率。
分块乘法的应用场景
图像处理
在图像处理中,分块乘法可以用 于图像压缩、图像变换等算法中 ,提高计算效率。
数值积分和微分
在数值分析中,分块处理被积函数或被微分函数,可以提高数值积分的精度和数值微分的稳定性 。
有限元分析
在有限元分析中,将连续的求解域划分为有限个小的、互不重叠的子域(即分块),然后在每个 子域上应用近似函数进行计算。
分块乘法在图像处理中的应用
图像压缩
通过将图像分块,可以对每个分块进行压缩编码, 从而实现图像的压缩存储和传输。
应用领域拓展
分块乘法将逐渐应用于更多领域,如机器学习、图像处理等。
分布式计算
利用分布式计算技术,实现大规模分块乘法的并行计算,提高计 算能力。
分块乘法的未来应用
科学计算
在科学计算领域,分块乘法将用于解决大规模线性方 程组、矩阵运算等问题。
数据处理
在数据处理中,分块乘法可用于加速大规模数据的分 析和处理。
图像增强
将图像分块后,可以对每个分块进行不同的处理, 从而实现图像的局部增强。
图像特征提取
将图像分块后,可以提取每个分块的特征,从而 进行图像识别、目标检测等任务。
04
分块乘法的优缺点分析
分块乘法的优点
计算效率高
分块乘法将大矩阵的乘法转换为多个小矩阵的乘法,减少 了计算量,提高了计算效率。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
目录一、题目及要求 (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 )。