mpi矩阵乘法(C=αAB+βC)

最近领导让把之前安装的软件lapack、blas里的dgemm运算提取出来独立作为一套程序,然后把这段程序改为并行的,并测试一下进程规模扩展到128时的并行效率。
        我发现这个是dgemm.f文件,里面主要是对C=αAB+βC的实现,因此在此总结一下MPI的矩阵乘法使用。
        其主要思想:是把相乘的矩阵按行分解(任务分解),分别分给不同的进程,然后在汇总到一个进程上,在程序上实现则用到了主从模式,人为的把进程分为主进程和从进程,主进程负责对原始矩阵初始化赋值,并把数据均匀分发(为了负载均衡)到从进程上进行相乘运算,主要用到的知识是MPI点对点通信和组通信的机制。

一、使用简单的MPI_Send和MPI_Recv实现

#include <stdio.h>
#include "mpi.h"
#include <stdlib.h>
#include "functions.h" #define M 1000 // 矩阵维度
#define N 1100
#define K 900 int main(int argc, char **argv)
{
int my_rank,comm_sz,line;
double start, stop; //计时时间
MPI_Status status;
char Processorname[20]; double *Matrix_A,*Matrix_B,*Matrix_C,*ans,*buffer_A,*buffer_C;
double alpha=2,beta=2; // 系数C=aA*B+bC MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
MPI_Comm_rank(MPI_COMM_WORLD,&my_rank); line=M/comm_sz; // 每个进程分多少行数据
Matrix_A=(double*)malloc(M*N*sizeof(double));
Matrix_B=(double*)malloc(N*K*sizeof(double));
Matrix_C=(double*)malloc(M*K*sizeof(double));
buffer_A=(double*)malloc(line*N*sizeof(double)); // A的均分行的数据
buffer_C=(double*)malloc(line*K*sizeof(double)); // C的均分行的数据
ans=(double*)malloc(line*K*sizeof(double)); // 临时保存部分数据计算结果 // 给矩阵A B,C赋值
if(my_rank==0){
start=MPI_Wtime();
for(int i=0;i<M;i++){
for(int j=0;j<N;j++)
Matrix_A[i*N+j]=i+1;
}
for(int i=0;i<N;i++){
for(int j=0;j<K;j++)
Matrix_B[i*K+j]=j+1;
}
for(int i=0;i<M;i++){
for(int j=0;j<K;j++)
Matrix_C[i*K+j]=1;
} // 输出A,B,C
/*Matrix_print(Matrix_A,M,N);
Matrix_print(Matrix_B,N,K);
Matrix_print(Matrix_C,M,K);
*/
/*将矩阵广播出去*/
for(int i=1;i<comm_sz;i++){
MPI_Send(Matrix_A+(i-1)*line*N,line*N,MPI_DOUBLE,i,66,MPI_COMM_WORLD);
MPI_Send(Matrix_C+(i-1)*line*K,line*K,MPI_DOUBLE,i,99,MPI_COMM_WORLD);
}
MPI_Bcast(Matrix_B,N*K,MPI_DOUBLE,0,MPI_COMM_WORLD); // 接收从进程的计算结果
for(int p=1;p<comm_sz;p++){
MPI_Recv(ans,line*K,MPI_DOUBLE,p,33,MPI_COMM_WORLD,&status);
for(int i=0;i<line;i+=comm_sz)
for(int j=0;j<K;j++)
Matrix_C[((p-1)*line+i)*K+j]=ans[i*K+j];
} // 计算A剩下的行数据
for(int i=(comm_sz-1)*line;i<M;i++){
for(int j=0;j<K;j++){
double temp=0;
for(int p=0;p<N;p++)
temp+=Matrix_A[i*N+p]*Matrix_B[p*K+j];
Matrix_C[i*K+j]=alpha*temp+beta*Matrix_C[i*K+j];
}
} //Matrix_print(Matrix_C,M,K);
stop=MPI_Wtime(); printf("rank:%d time:%lfs\n",my_rank,stop-start); free(Matrix_A);
free(Matrix_B);
free(Matrix_C);
free(buffer_A);
free(buffer_C);
free(ans);
}
else{
//接收广播的数据
MPI_Recv(buffer_A,line*N,MPI_DOUBLE,0,66,MPI_COMM_WORLD,&status);
MPI_Recv(buffer_C,line*K,MPI_DOUBLE,0,99,MPI_COMM_WORLD,&status);
MPI_Bcast(Matrix_B,N*K,MPI_DOUBLE,0,MPI_COMM_WORLD); //计算乘积结果,并将结果发送给主进程
for(int i=0;i<line;i++){
for(int j=0;j<K;j++){
double temp=0;
for(int p=0;p<N;p++){
temp+=buffer_A[i*N+p]*Matrix_B[p*K+j];
}
ans[i*line+j]=alpha*temp+beta*buffer_C[i*K+j];
}
}
MPI_Send(ans,line*K,MPI_DOUBLE,0,33,MPI_COMM_WORLD);
} MPI_Finalize();
return 0;
}

二、使用较高级的MPI_Scatter和MPI_Gather实现

#include <stdio.h>
#include "mpi.h"
#include <stdlib.h>
#include "functions.h" #define M 1200 // 矩阵维度
#define N 1000
#define K 1100 int main(int argc, char **argv)
{
int my_rank,comm_sz,line;
double start, stop; //计时时间
MPI_Status status; double *Matrix_A,*Matrix_B,*Matrix_C,*ans,*buffer_A,*buffer_C,*result_Matrix;
double alpha=2,beta=2; // 系数C=aA*B+bC MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
MPI_Comm_rank(MPI_COMM_WORLD,&my_rank); line=M/comm_sz; // 每个进程分多少行数据
Matrix_A=(double*)malloc(M*N*sizeof(double));
Matrix_B=(double*)malloc(N*K*sizeof(double));
Matrix_C=(double*)malloc(M*K*sizeof(double));
buffer_A=(double*)malloc(line*N*sizeof(double)); // A的均分行的数据
buffer_C=(double*)malloc(line*K*sizeof(double)); // C的均分行的数据
ans=(double*)malloc(line*K*sizeof(double)); // 保存部分数据计算结果
result_Matrix=(double*)malloc(M*K*sizeof(double)); // 保存数据计算结果 // 给矩阵A B,C赋值
if(my_rank==0){
start=MPI_Wtime();
for(int i=0;i<M;i++){
for(int j=0;j<N;j++)
Matrix_A[i*N+j]=i+1;
for(int p=0;p<K;p++)
Matrix_C[i*K+p]=1;
}
for(int i=0;i<N;i++){
for(int j=0;j<K;j++)
Matrix_B[i*K+j]=j+1;
} // 输出A,B,C
//Matrix_print(Matrix_A,M,N);
//Matrix_print(Matrix_B,N,K);
//Matrix_print(Matrix_C,M,K);
} // 数据分发
MPI_Scatter(Matrix_A,line*N,MPI_DOUBLE,buffer_A,line*N,MPI_DOUBLE,0,MPI_COMM_WORLD);
MPI_Scatter(Matrix_C,line*K,MPI_DOUBLE,buffer_C,line*K,MPI_DOUBLE,0,MPI_COMM_WORLD);
// 数据广播
MPI_Bcast(Matrix_B,N*K,MPI_DOUBLE,0,MPI_COMM_WORLD); // 计算 结果
for(int i=0;i<line;i++){
for(int j=0;j<K;j++){
double temp=0;
for(int p=0;p<N;p++)
temp+=buffer_A[i*N+p]*Matrix_B[p*K+j];
ans[i*K+j]=alpha*temp+beta*buffer_C[i*K+j];
}
}
// 结果聚集
MPI_Gather(ans,line*K,MPI_DOUBLE,result_Matrix,line*K,MPI_DOUBLE,0,MPI_COMM_WORLD); // 计算A剩下的行数据
if(my_rank==0){
int rest=M%comm_sz;
if(rest!=0){
for(int i=M-rest-1;i<M;i++)
for(int j=0;j<K;j++){
double temp=0;
for(int p=0;p<N;p++)
temp+=Matrix_A[i*N+p]*Matrix_B[p*K+j];
result_Matrix[i*K+j]=alpha*temp+beta*Matrix_C[i*K+j];
}
} //Matrix_print(result_Matrix,M,K);
stop=MPI_Wtime(); printf("rank:%d time:%lfs\n",my_rank,stop-start);
} free(Matrix_A);
free(Matrix_B);
free(Matrix_C);
free(ans);
free(buffer_A);
free(buffer_C);
free(result_Marix); MPI_Finalize();
return 0;
}  

三、结果分析

下图为上面两种方法的耗时:

1、 执行时间分析:
并行时,随着进程数目的增多,并行计算的时间越来越短;当达到一定的进程数时,执行时间小到最小值;然后再随着进程数的增多,执行时间反而越来越长。
2、加速比分析:
随着进程数的增大,加速比也是逐渐增大到最大值;再随着进程数的增大,加速比逐渐减小。
3、执行效率分析:
随着进程数的增大,程序执行效率不断降低

由于消息传递需要成本,而且不是每个进程都同时开始和结束,所以随着进程数的上升,平均每进程的效率下降

四、头文件functions.h内容

/********** 输出函数 **********/
void Matrix_print(double *A,int M,int N)
{
for(int i=0;i<M;i++){
for(int j=0;j<N;j++)
printf("%.1f ",A[i*N+j]);
printf("\n");
}
printf("\n");
}

  

结束。

MPI学习笔记(二):矩阵相乘的两种实现方法的更多相关文章

  1. javaweb学习总结(二十一)——JavaWeb的两种开发模式

    SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...

  2. Linux学习笔记21——线程同步的两种方式

    一  用信号量同步 1 信号量函数的名字都以sem_开头,线程中使用的基本信号量函数有4个 2 创建信号量 #include<semaphore.h> int sem_init(sem_t ...

  3. python学习笔记30(全局变量的两种解决办法)

    先看程序: >>> count = 0 >>> def fuc(count): print count count +=1 >>> for i i ...

  4. LINUX编程学习笔记(十三) 遍历目录的两种方法

    1 默认情况下  实际用户和有效用户是一样的 实际用户:执行用户   有效用户:权限用户 getuid()  实际用户 geteuid() 有效用户 chmod u+s 之后 ,其他人执行文件时,实际 ...

  5. TensorFlow+实战Google深度学习框架学习笔记(10)-----神经网络几种优化方法

    神经网络的优化方法: 1.学习率的设置(指数衰减) 2.过拟合问题(Dropout) 3.滑动平均模型(参数更新,使模型在测试数据上更鲁棒) 4.批标准化(解决网络层数加深而产生的问题---如梯度弥散 ...

  6. Spring学习笔记:spring与mybatis四种整合方法

    1.采用数据映射器(MapperFactoryBean)的方式,不用写mybatis映射文件,采用注解方式提供相应的sql语句和输入参数.  (1)Spring配置文件: <!-- 引入jdbc ...

  7. tensorflow学习笔记二:入门基础 好教程 可用

    http://www.cnblogs.com/denny402/p/5852083.html tensorflow学习笔记二:入门基础   TensorFlow用张量这种数据结构来表示所有的数据.用一 ...

  8. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  9. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

随机推荐

  1. python使用虚拟环境venv

    venv模块支持使用自己的站点目录创建轻量级"虚拟环境",可选择与系统站点目录隔离.每个虚拟环境都有自己的Python二进制文件(与用于创建此环境的二进制文件的版本相匹配),并且可 ...

  2. 自增特性,外键,级联更新与级联删除,表间关系,SELECT用法,GROUP BY

    自增特性 自动增长的作用: 问题:为数据表设置主键约束后,每次插入记录时,如果插入的值已经存在,会插入失败. 如何解决:为主键生成自动增长的值. 自动增长的语法: 字段名 数据类型 AUTO_INCR ...

  3. JS运算符,流程控制,函数,内置对象,BOM与DOM

    运算符 1.算数运算符 运算符 描述 + 加 - 减 * 乘 / 除 % 取余(保留整数) ++ 递加 - - 递减 ** 幂 var x=10; var res1=x++; '先赋值后自增1' 10 ...

  4. Redis设计与实现3.2:Sentinel

    Sentinel哨兵 这是<Redis设计与实现>系列的文章,系列导航:Redis设计与实现笔记 哨兵:监视.通知.自动故障恢复 启动与初始化 Sentinel 的本质只是一个运行在特殊模 ...

  5. 重新认识 MSBuild - 1

    前言 很多人一谈到 MSBuild,脑子里就会出现 "XML"."只能用 VS 的属性框图形界面操作"."可定制性和扩展性差" 和 &quo ...

  6. 445. Add Two Numbers II - LeetCode

    Question 445. Add Two Numbers II Solution 题目大意:两个列表相加 思路:构造两个栈,两个列表的数依次入栈,再出栈的时候计算其和作为返回链表的一个节点 Java ...

  7. Redis 中的原子操作(1)-Redis 中命令的原子性

    Redis 如何应对并发访问 Redis 中处理并发的方案 原子性 Redis 的编程模型 Unix 中的 I/O 模型 thread-based architecture(基于线程的架构) even ...

  8. 奶盖拌饭 NKOJ8457

    题意:一个无向图,每次询问给出一条边,问这条边的最大值满足这条边一定在这个图的最小生成树上,如果没有上限输出-1. 思路:考场上想过的,将分为两类,(非)树边. 1.亿点性质 非树边:加上这条边所构成 ...

  9. 变量作用域——JavaSE基础

    变量作用域 局部变量.成员变量.静态变量的区别 类型 声明位置 从属于 生命周期 局部变量 方法或语句块内部 方法/语句块 从声明位置开始,直到方法或语句块执行完毕,局部变量消失 成员变量 (实例变量 ...

  10. 【二分图】匈牙利 & KM

    [二分图]匈牙利 & KM 二分图 概念: 一个图 \(G=(V,E)\) 是无向图,如果顶点 \(V\) 可以分成两个互不相交地子集 \(X,Y\) 且任意一条边的两个顶点一个在 \(X\) ...