原理不解释,直接上代码

代码中被注释的源程序可用于打印中间结果,检查运算是否正确。

  1. #include "mpi.h"
  2. #include <math.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. void scatter_matrix(int* fstream,int n1,int n2,int*Q,int root,int tag){
  8. /*每个矩阵块的大小*/
  9. int rows=(n1+root-1)/root;
  10. int cols=(n2+root-1)/root;
  11. int* tmp_matrix=(int*)malloc(rows*cols*sizeof(int));
  12.  
  13. int i,j;
  14. memset(Q,0,rows*cols*sizeof(int));
  15. for(i=0;i<root;i++){
  16. for(j=0;j<root;j++){
  17. int p=0,q=0;
  18. int imin=i*rows*n2;
  19. int jmin=j*cols;
  20. memset(tmp_matrix,0,sizeof(tmp_matrix));
  21.  
  22. /*在划分矩阵时,由于地空间不连续,需要另开辟一个数组连续的保存起来,以便于调用MPI_Send*/
  23. for(p=0;p<rows;p++,imin+=n2){
  24. for(q=0;q<cols;q++){
  25. tmp_matrix[p*cols+q]=fstream[imin+jmin+q];
  26. }
  27. }
  28. if(i==0&&j==0){
  29. /*进程0 不需要使用MPI_Send将数据发送给自己,直接使用memcpy将结果拷贝即可*/
  30. memcpy(Q,tmp_matrix,rows*cols*sizeof(int));
  31. }else{
  32. /*将分块发送给位于i行,j列的进程*/
  33. MPI_Send(tmp_matrix,rows*cols,MPI_INT,i*root+j,tag,MPI_COMM_WORLD);
  34. }
  35. }
  36. }
  37. }
  38. /*
  39. *@row:矩阵所在的行
  40. *@col:矩阵所在的列
  41. *@sp:sp=root=sqrt(nprocs)
  42. *@return 根据行列号计算进程实际编号
  43. */
  44. int get_index(int row,int col,int sp){
  45. int tmp=((row+sp)%sp)*sp+(col+sp)%sp;
  46. return tmp;
  47. }
  48. /*计算矩阵乘法,将结果存入C中*/
  49. void matrix_multi(int* A,int *B,int *C,int n1,int n2,int n3,int myid){
  50. int i=0,j=0,k=0;
  51. int* tmp_C=(int*)malloc(n1*n3*sizeof(int));
  52. memset(tmp_C,0,sizeof(int)*n1*n3);
  53.  
  54. for(i=0;i<n1;i++){
  55. for(j=0;j<n3;j++){
  56. for(k=0;k<n2;k++){
  57. tmp_C[i*n3+j]+=A[i*n2+k]*B[k*n3+j];
  58. }
  59. C[i*n3+j]+=tmp_C[i*n3+j];
  60. }
  61.  
  62. }
  63.  
  64. }
  65.  
  66. /*用于矩阵下标定位对齐*/
  67. void shuffle(int*A,int*buf_A,int buf_A_size,int *B,int*buf_B,int buf_B_size,int root,int myid){
  68. int i,j;
  69. MPI_Status status;
  70. int cur_col=0;
  71. int cur_row=0;
  72. /*通过进程编号计算获得当前进程所在的行号和列号*/
  73. cur_row=myid/root;
  74. cur_col=myid-cur_row*root;
  75. /*对于矩阵A,第i行的矩阵需要向左平移i次*/
  76. for(i=0;i<cur_row;i++){
  77. /*接收来自右边的数据,并将当前矩阵发送给左边的进程*/
  78. MPI_Sendrecv(A,buf_A_size,MPI_INT,get_index(cur_row,cur_col-1,root),102,
  79. buf_A,buf_A_size,MPI_INT,get_index(cur_row,cur_col+1,root),102,MPI_COMM_WORLD,&status);
  80. memcpy(A,buf_A,buf_A_size*sizeof(int));/*buf_A用于通信时缓存矩阵*/
  81. memset(buf_A,0,buf_A_size*sizeof(int));
  82. }
  83. /*对于矩阵B,第j列的矩阵需要向上平移j次*/
  84. for(j=0;j<cur_col;j++){
  85. /*接收来自下边的数据,并将当前矩阵发送给上边的进程*/
  86. MPI_Sendrecv(B,buf_B_size,MPI_INT,get_index(cur_row-1,cur_col,root),103,
  87. buf_B,buf_B_size,MPI_INT,get_index(cur_row+1,cur_col,root),103,MPI_COMM_WORLD,&status);
  88. memcpy(B,buf_B,buf_B_size*sizeof(int));/*buf_B用于通信时缓存矩阵*/
  89. memset(buf_B,0,buf_B_size*sizeof(int));
  90. }
  91. /*printf("I have shuffled!\n");*/
  92. }
  93. void cannon(int*A,int*buf_A,int buf_A_size,int *B,int*buf_B,int buf_B_size,
  94. int *C,int buf_C_size,int row_a,int col_a,int col_b,int root,int myid){
  95. MPI_Status status;
  96. double elapsed_time,multiply_time=0,passdata_time=0;
  97. int i,j;
  98. memset(C,0,sizeof(int)*buf_C_size);
  99. int cur_col=0;
  100. int cur_row=0;
  101. /*通过进程编号计算获得当前进程所在的行号和列号*/
  102. cur_row=myid/root;
  103. cur_col=myid-cur_row*root;
  104.  
  105. for(i=0;i<root;i++){/*一共需要循环root次,root=sqrt(nprocs)*/
  106. elapsed_time=MPI_Wtime();
  107. matrix_multi(A,B,C,row_a,col_a,col_b,myid);/*计算矩阵乘法*/
  108. elapsed_time=MPI_Wtime()-elapsed_time;
  109. multiply_time+=elapsed_time;
  110. /*elapsed_time=MPI_Wtime(); */
  111. /*接收来自右边(row,col+1)的数据,并将当前矩阵发送给左边(row,col-1)的进程*/
  112. MPI_Sendrecv(A,buf_A_size,MPI_INT,get_index(cur_row,cur_col-1,root),102,
  113. buf_A,buf_A_size,MPI_INT,get_index(cur_row,cur_col+1,root),102,MPI_COMM_WORLD,&status);
  114. /*接收来自下边(row+1,col)的数据,并将当前矩阵发送给上边(row-1,col)的进程*/
  115. MPI_Sendrecv(B,buf_B_size,MPI_INT,get_index(cur_row-1,cur_col,root),103,
  116. buf_B,buf_B_size,MPI_INT,get_index(cur_row+1,cur_col,root),103,MPI_COMM_WORLD,&status);
  117. /*elapsed_time=MPI_Wtime()-elapsed_time;
  118. passdata_time+=elapsed_time;*/
  119. memcpy(B,buf_B,buf_B_size*sizeof(int));/*将buf_B中的数据拷贝至B中*/
  120. memcpy(A,buf_A,buf_A_size*sizeof(int));/*将buf_A中的数据拷贝至A中*/
  121.  
  122. }
  123. /*将计算结果发送给数组C*/
  124. MPI_Send(C,row_a*col_b,MPI_INT,0,104,MPI_COMM_WORLD);
  125.  
  126. printf("proc:%d, passdata time:%lf multiply time:%lf\n",myid,passdata_time,multiply_time);
  127. }
  128.  
  129. void gather_matrix(int *fstream,int n1,int n3,int*C,int root,FILE*fhc){
  130. MPI_Status status;
  131. int rows=(n1+root-1)/root;
  132. int cols=(n3+root-1)/root;
  133. int* tmp_matrix=(int*)malloc(rows*cols*sizeof(int));
  134. int i,j;
  135.  
  136. for(i=0;i<root;i++){
  137. for(j=0;j<root;j++){
  138. int p,q;
  139. int imin=i*rows*n3;
  140. int jmin=j*cols;
  141. memset(tmp_matrix,0,sizeof(tmp_matrix));
  142. /*接收来自各个进程的数据*/
  143. MPI_Recv(tmp_matrix,rows*cols,MPI_INT,i*root+j,104,MPI_COMM_WORLD,&status);
  144. /*printf("I am passed proc:%d \n",i*root+j);*/
  145. /*将接收的矩阵tmp拼接到矩阵C中去,需要按照合理顺序拼接,否则结果会出错*/
  146. for(p=0;p<rows;p++,imin+=n3){
  147. for(q=0;q<cols;q++){
  148. fstream[imin+jmin+q]=tmp_matrix[p*cols+q];
  149. /*printf("%d ",((int*)fstream)[imin+jmin+q]);*/
  150. }
  151. /*printf("\n");*/
  152. }
  153. }
  154. }
  155.  
  156. /*将结果打印到文件中*/
  157. for(i=0;i<n1;i++){
  158. for(j=0;j<n3;j++){
  159. fprintf(fhc,"%d ",fstream[i*n3+j]);
  160. }
  161. fprintf(fhc,"\n");
  162. }
  163. }
  164.  
  165. int main(int argc,char**argv){
  166. int myid,numprocs;
  167. int i,j;
  168. MPI_Status status;
  169. int root=0;
  170. int dim[3];
  171. double elapsed_time=0;
  172. int max_rows_a,max_cols_a,max_rows_b,max_cols_b;
  173. int buf_A_size,buf_B_size,buf_C_size;
  174. FILE* fhc;
  175. /*suppose A:n1*n2 ,B:n2*n3;n1,n2,n3 are read from input file*/
  176. int n1,n2,n3;
  177. /*buffer for matrix A,B,C will be shifted ,so they each have two buffer*/
  178. int *A,*B,*C,*buf_A,*buf_B;
  179.  
  180. /*on proc0,buffers to cache matrix files of A,B and C*/
  181. int *fstream_a=NULL,*fstream_b=NULL,*fstream_c=NULL;
  182. MPI_Init(&argc,&argv);/*初始化*/
  183. MPI_Comm_rank(MPI_COMM_WORLD,&myid);/*获取当前进程ID*/
  184. MPI_Comm_size(MPI_COMM_WORLD,&numprocs);/*获取全部进程数量*/
  185.  
  186. root=sqrt(numprocs);
  187. if(numprocs!=root*root){
  188. /*如果进程总数不是平方数,则结束程序*/
  189. printf("process number must be a squre!\n");
  190. exit(-1);
  191. }
  192.  
  193. /*on proc0,preprocess the command line,read in file
  194. for A,B and put their sizes in dim[]*/
  195. if(myid==0){
  196. FILE *file_a,*file_b,*file_c;
  197. int n1,n2,n3;
  198. int i,j;
  199. file_a=fopen(argv[1],"r");/*打开文件a,文件名从运行时给的参数中获得*/
  200. file_b=fopen(argv[2],"r");/*打开文件b,文件名从运行时给的参数中获得*/
  201. fscanf(file_a,"%d %d",&n1,&n2);/*从文件a中读取矩阵A的行数,列数*/
  202. fscanf(file_b,"%d %d",&n2,&n3);/*从文件b中读取矩阵B的行数,列数*/
  203.  
  204. dim[0]=n1,dim[1]=n2,dim[2]=n3;
  205. fstream_a=(int*)malloc(n1*n2*sizeof(int));/*分配一块内存,用于将矩阵A读入内存*/
  206. fstream_b=(int*)malloc(n2*n3*sizeof(int));/*分配一块内存,用于将矩阵B读入内存*/
  207. /*printf("Yeah! I got n1=%d,n2=%d,n3=%d\n",n1,n2,n3);*/
  208. /*读入矩阵A,保存在fstream_a中*/
  209. for(i=0;i<n1;i++)
  210. for(j=0;j<n2;j++)
  211. fscanf(file_a,"%d",&((int*)fstream_a)[i*n2+j]);
  212. /*读入矩阵B,保存在fstream_b中*/
  213. for(i=0;i<n2;i++)
  214. for(j=0;j<n3;j++)
  215. fscanf(file_b,"%d",&((int*)fstream_b)[i*n3+j]);
  216. }
  217. /*将矩阵的行数,列数通过Bcast广播给所有进程*/
  218. MPI_Bcast(dim,3,MPI_INT,0,MPI_COMM_WORLD);
  219. n1=dim[0],n2=dim[1],n3=dim[2];
  220.  
  221. /*begin new version*/
  222.  
  223. max_rows_a=(n1+root-1)/root;/*子矩阵块A的行数*/
  224. max_cols_a=(n2+root-1)/root;/*子矩阵块A的列数*/
  225. max_rows_b=max_cols_a; /*子矩阵块B的行数*/
  226. max_cols_b=(n3+root-1)/root;/*子矩阵块B的列数*/
  227. buf_A_size=max_rows_a*max_cols_a;/*子矩阵块A的大小*/
  228. buf_B_size=max_rows_b*max_cols_b;/*子矩阵块B的大小*/
  229. buf_C_size=max_rows_a*max_cols_b;/*子矩阵块C的大小*/
  230.  
  231. /*给A,,buf_A,buf_B,B,C分配内存空间,其中buf_A,buf_B用于通讯中的缓存*/
  232. A=(int*)malloc(sizeof(int)*buf_A_size);
  233. buf_A=(int*)malloc(sizeof(int)*buf_A_size);
  234. B=(int*)malloc(sizeof(int)*buf_B_size);
  235. buf_B=(int*)malloc(sizeof(int)*buf_B_size);
  236. C=(int*)malloc(sizeof(int)*buf_C_size);
  237. if(A==NULL||buf_A==NULL||B==NULL||buf_B==NULL||C==NULL)
  238. {
  239. /*如果内存申请失败,就退出*/
  240. printf("Memory allocation failed!\n");
  241. exit(-1);
  242. }
  243.  
  244. /*proc 0 scatter A,B to other procs in a 2D block distribution fashion*/
  245. if(myid==0){
  246. /*printf("max_rows_a:%d\n",max_rows_a);
  247. printf("max_cols_a:%d\n",max_cols_a);
  248. printf("max_rows_b:%d\n",max_rows_b);
  249. printf("max_cols_b:%d\n",max_cols_b);*/
  250. /*进程0 将矩阵A,B划分成小块,分发给其他进程*/
  251. scatter_matrix((int*)fstream_a,n1,n2,A,root,100);
  252. /*printf("I am debuging!\n");*/
  253. scatter_matrix((int*)fstream_b,n2,n3,B,root,101);
  254. /*printf("I am finding fault!\n");*/
  255. }else{
  256. /*其他进程接收来自进程0 发送的矩阵A,B*/
  257. MPI_Recv(A,max_rows_a*max_cols_a,MPI_INT,0,100,MPI_COMM_WORLD,&status);
  258. MPI_Recv(B,max_rows_b*max_cols_b,MPI_INT,0,101,MPI_COMM_WORLD,&status);
  259. }
  260.  
  261. MPI_Barrier(MPI_COMM_WORLD);/*等待全部进程完成数据接收工作。*/
  262.  
  263. /*printf("I am proc %d\n",myid);
  264. for(i=0;i<max_rows_a;i++){
  265. printf("%d: ",myid);
  266. for(j=0;j<max_cols_a;j++){
  267. printf("%d ",A[i*max_cols_a+j]);
  268. }
  269. printf("\n");
  270. }
  271. printf("I am proc %d\n",myid);
  272. for(i=0;i<max_rows_b;i++){
  273. printf("%d: ",myid);
  274. for(j=0;j<max_cols_b;j++){
  275. printf("%d ",B[i*max_cols_b+j]);
  276. }
  277. printf("\n");
  278. }*/
  279.  
  280. /*compute C=A*B by Cannon algorithm*/
  281. /*矩阵块必须定位对齐,先做预处理*/
  282. shuffle(A,buf_A,buf_A_size,B,buf_B,buf_B_size,root,myid);
  283. elapsed_time=MPI_Wtime();
  284. /*包含cannon全部内容*/
  285. cannon(A,buf_A,buf_A_size,B,buf_B,buf_B_size,
  286. C,buf_C_size,max_rows_a,max_cols_a,max_cols_b,root,myid);
  287. MPI_Barrier(MPI_COMM_WORLD);
  288. elapsed_time=MPI_Wtime()-elapsed_time;/*统计cannon算法实际耗时*/
  289.  
  290. MPI_Barrier(MPI_COMM_WORLD);/*等待所有进程完成cannon算法,将结果发送给进程0*/
  291.  
  292. int fsize_c=sizeof(int)*n1*n3;
  293. if(myid==0){
  294. /*进程0创建文件写句柄,准备将计算结果写入文件中*/
  295. if(!(fhc=fopen(argv[3],"w"))){
  296. printf("Cant't open file %s\n",argv[3]);
  297. MPI_Finalize();
  298. }
  299. fstream_c=(int*)malloc(fsize_c);
  300. /*进程0 接收来自各个进程的结果矩阵,拼接成一个完整的结果,写入文件,持久化数据结果*/
  301. gather_matrix(fstream_c,n1,n3,C,root,fhc);
  302. }
  303.  
  304. MPI_Barrier(MPI_COMM_WORLD); /*make sure proc 0 read all it needs*/
  305.  
  306. if(myid==0){
  307. int i,j;
  308. printf("Cannon algorithm :multiply a %d* %d with a %d*%d, use %lf(s)\n",
  309. n1,n2,n2,n3,elapsed_time);
  310. /*printf("I have finished!\n");*/
  311. fclose(fhc);/*关闭文件读写句柄*/
  312. /*释放申请的内存空间*/
  313. free(fstream_a);
  314. free(fstream_b);
  315. free(fstream_c);
  316. }
  317.  
  318. /*释放申请的内存空间*/
  319. free(A);free(buf_A);
  320. free(B);free(buf_B);
  321. free(C);
  322. MPI_Finalize();
  323. return 0;
  324. }

Parallel Computing–Cannon算法 (MPI 实现)的更多相关文章

  1. MPI中的cannon算法

    Cannon算法 算法过程 假设矩阵\(A,B\)和\(C\)都可以分成\(m\times m\)块矩阵,即\(A = (A_{(ij)})_{m\times m},B = (B_{(ij)})_{m ...

  2. Introduction to Parallel Computing

    Copied From:https://computing.llnl.gov/tutorials/parallel_comp/ Author: Blaise Barney, Lawrence Live ...

  3. Parallel.For 平行算法 使用

    之前看到Parallel的用法,觉得很高深,很腻害,今天专门抽空研究了一下,发现还是很easy的. .NET Framework 4.0 新加的功能,所以4.0之前的无法使用哦. 下面介绍一下,Par ...

  4. General mistakes in parallel computing

    这是2013年写的一篇旧文,放在gegahost.net上面  http://raison.gegahost.net/?p=97 March 11, 2013 General mistakes in ...

  5. Method and apparatus for an atomic operation in a parallel computing environment

    A method and apparatus for a atomic operation is described. A method comprises receiving a first pro ...

  6. MPI编程——分块矩阵乘法(cannon算法)

    https://blog.csdn.net/a429367172/article/details/88933877

  7. STROME --realtime & online parallel computing

    Data Collections ---> Stream to Channel (as source input) ----> Parallel Computing---> Resu ...

  8. Distributed and Parallel Computing

    Omega Network Model

  9. [译]何时使用 Parallel.ForEach,何时使用 PLINQ

    原作者: Pamela Vagata, Parallel Computing Platform Group, Microsoft Corporation 原文pdf:http://download.c ...

随机推荐

  1. C/C++实践笔记_002编译和链接

    1.要卡死程序用异步,同步的话开一个就关一个值为非0死循环.预处理优先于编译,别称预编译main函数死循环2.程序总是从main函数开始执行的C语言本身不提供输入输出语句print等来自于stdio库 ...

  2. 利用PowerShell+Jenkins,实现项目的自动化部署

    当项目越来越庞大,部署环境越来越多以后,就会越来越依赖于自动化.比如本人公司的项目,目前有6个web和4个windows service,同时本地有两套环境:开发自测试环境和QA测试环境.每次版本发布 ...

  3. 探究JVM——垃圾回收

    垃圾回收主要考虑三件事情:哪些内存需要回收?什么时候回收?如何回收? 一.哪些内存需要回收? 堆内存:对于JVM 来说,垃圾回收主要是针对堆内存中的对象实例. 方法区:垃圾收集行为在方法区是比较少出现 ...

  4. PS技能大全

    1.设置固定的图片大小 参考:http://jingyan.baidu.com/album/642c9d3418bec4644a46f72a.html?picindex=1

  5. Keepalived+Redis高可用部署(第二版)

    更新 20150625 脚本由5个减少为4个,sh脚本指令做了精简. 修改了另外3个脚本,在日志里增加了日期显示. 新增redis数据类型,持久化,主从同步简介. 新增hiredis简介. 新增c语言 ...

  6. 搭建企业内部yum仓库(centos6+centos7+epel源)

    搭建自己的yum仓库,将自己制作好的rpm包,添加到自己的yum源中. yum仓库服务端配置如下 : 1. 创建yum仓库目录 mkdir -p /data/yum_data/cd /data/yum ...

  7. Android复习笔记--架构与版本

    #Android架构: 1. Linux 内核层 Android 系统是基于Linux 2.6 内核的,这一层为Android 设备的各种硬件提供了底 层的驱动,如显示驱动.音频驱动.照相机驱动.蓝牙 ...

  8. 基于tiny4412原生uboot修改制作SD启动并烧写到emmc

    最近入手tiny4412的标准板,底板SDK型号为1506.但是因为友善之臂提供的superboot不能进入boot菜单,此时我就不能通过tftp下载内核和通过nfs挂载根文件系统,于是想自己做个ub ...

  9. Java--笔记(2)

    11.根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件: 1)互斥条件(Mutual ...

  10. SpringMVC学习系列(8) 之 国际化

    一.基于浏览器请求的国际化实现: 1)在 spring的配置文件中添加 <bean id="messageSource" class="org.springfram ...