mpi大规模矩阵乘法C语言,C语言基于MPI并行计算矩阵的乘法

浏览量:

1,030

(在苹果系统下,如果文章中的图片不能正常显示,请升级Safari浏览器到最新版本,或者使用Chrome、Firefox浏览器打开。)

矩阵的计算是并行计算里的一个很重要的问题。矩阵是一种网格化的数据,是一组同类型数值的集合,矩阵的出现,使得代数系统更完善,对各种实际问题的求解产生了巨大的作用。但是其庞大的计算量往往令人生畏,稍微大一点的矩阵,计算就变得非常繁琐,不仅仅是人,而且串行计算的程序,也同样会变得非常缓慢。这时我们就需要并行计算来解决这些问题了。

本文使用分治思想,利用主从节点方式,实现了一个并行计算的矩阵相乘程序,并将结果跟串行程序做了对比,计算了其加速比。

程序设计思路

将一个矩阵按行划分子集,分摊给其他每个节点来计算,每个节点按照行和列的对应关系分别计算一部分结果,然后主节点将每个子节点的计算结果汇总起来。

并行实现 1.cpp

#include

#include

#include

#include

#define N 2000

#define FROM_MASTER 1

#define FROM_SLAVE 2

int A[N][N], B[N][N];

unsigned long long C[N][N];

MPI_Status status;//消息接收状态变量,存储也是分布的

int main(int argc, char **argv)

{

intprocess_num; //进程数,该变量为各处理器中的同名变量, 存储是分布的

intprocess_id;

intslave_num;

intdest; //目的进程标识号

intsource; //发送数据进程的标识号

introws;

introw_aver;

intremainder;

intoffset;//行偏移量

inti, j, k;

double start_time, end_time;

srand((unsigned int)time(NULL));

MPI_Init(&argc, &argv);//初始化MPI

/*该函数被各进程各调用一次,得到各自的进程id值*/

MPI_Comm_rank(MPI_COMM_WORLD, &process_id);

/*该函数被各进程各调用一次,得到进程数*/

MPI_Comm_size(MPI_COMM_WORLD, &process_num);

slave_num = process_num - 1;

if(process_id == 0)

{

for (i=0; i

{

for (j=0; j

{

A[i][j] = rand() % 10;

B[i][j] = rand() % 10;

C[i][k] = 0;

}

}

row_aver = N / slave_num;

remainder = N % slave_num;

offset = 0;

double start_time, end_time;

double t_duration;

clock_t t_start, t_finish;

t_start = clock();

//start_time = MPI_Wtime();

//有的程序是将时间函数放在这个for循环的两边

for(dest=1; dest<=slave_num; dest++)

{

rows = (dest <= remainder) ? row_aver+1 : row_aver;

printf("sending %d rows to process %d\n", rows, dest);

MPI_Send(&offset, 1, MPI_INT, dest, FROM_MASTER, MPI_COMM_WORLD);

MPI_Send(&rows, 1, MPI_INT, dest, FROM_MASTER, MPI_COMM_WORLD);

MPI_Send(&A[offset][0], rows*N, MPI_INT, dest, FROM_MASTER, MPI_COMM_WORLD);

MPI_Send(&B, N*N, MPI_INT, dest, FROM_MASTER, MPI_COMM_WORLD);

offset += rows;

}

for(source=1; source<=slave_num; source++)

{

MPI_Recv(&offset, 1, MPI_INT, source, FROM_SLAVE, MPI_COMM_WORLD, &status); //接收行偏移量

MPI_Recv(&rows, 1, MPI_INT, source, FROM_SLAVE, MPI_COMM_WORLD, &status); //接收行数

MPI_Recv(&C[offset][0], rows*N, MPI_UNSIGNED_LONG_LONG, source, FROM_SLAVE, MPI_COMM_WORLD, &status); //C接收从进程发回的结果

}

//end_time = MPI_Wtime();

t_finish = clock();

t_duration = (double)(t_finish - t_start) / CLOCKS_PER_SEC;

printf( "%f seconds\n", t_duration );

//printf("process cost %f seconds\n", end_time-start_time);

}

if(process_id > 0)

{

for (i=0; i

{

for (j=0; j

{

A[i][j] = 0;

B[i][j] = 0;

C[i][k] = 0;

}

}

MPI_Recv(&offset, 1, MPI_INT, 0, FROM_MASTER, MPI_COMM_WORLD, &status);

MPI_Recv(&rows, 1, MPI_INT, 0, FROM_MASTER, MPI_COMM_WORLD, &status);

MPI_Recv(&A, rows*N, MPI_INT, 0, FROM_MASTER, MPI_COMM_WORLD, &status);

MPI_Recv(&B, N*N, MPI_INT, 0, FROM_MASTER, MPI_COMM_WORLD, &status);

//double start_time, end_time;

//start_time = MPI_Wtime();

printf("rows is %d\n",rows);

for(i=0; i

{

for (k=0; k

{

int tmp = A[i][k];

for (j=0; j

{

C[i][j] += tmp*B[k][j];

}

}

}

//end_time = MPI_Wtime();

//printf( "other jiedian compute %f seconds\n", end_time-start_time);

MPI_Send(&offset, 1, MPI_INT, 0, FROM_SLAVE, MPI_COMM_WORLD); //将行偏移量发回主进程

MPI_Send(&rows, 1, MPI_INT, 0, FROM_SLAVE, MPI_COMM_WORLD); //将行数发回主进程

MPI_Send(&C, rows*N, MPI_UNSIGNED_LONG_LONG, 0, FROM_SLAVE, MPI_COMM_WORLD); //将计算得到的值发回主进程

}

printf("process_id %d closed \n",process_id);

/*关闭MPI,标志并行代码段的结束*/

MPI_Finalize();

return 0;

}

串行实现 0.cpp

#include

#include

#include

#define N 2000

int A[N][N], B[N][N];

unsigned long long C[N][N];

int main(int argc, char **argv)

{

inti, j, k;

srand((unsigned int)time(NULL));

for (i=0; i

{

for (j=0; j

{

A[i][j] = rand() % 10;

B[i][j] = rand() % 10;

C[i][k] = 0;

}

}

clock_t t_start, t_finish;

t_start = clock();

for (i=0; i

{

for (k=0; k

{

for (j=0; j

{

C[i][j] += A[i][k]*B[k][j];

}

}

}

double t_duration;

t_finish = clock();

t_duration = (double)(t_finish - t_start) / CLOCKS_PER_SEC;

printf( "%f seconds\n", t_duration );

return 0;

}

程序运行结果与分析

启动进程节点数

计算任务量

时间开销 (s)

(串行)

2000*2000

33.949744

2

2000*2000

29.764228

3

2000*2000

15.269486

5

2000*2000

7.996483

9

2000*2000

4.173008

17

2000*2000

2.451103

33

2000*2000

1.873135

加速比(3) = 2.2234

加速比(5) = 4.2456

加速比(9) = 8.1356

加速比(17) = 13.851

加速比(33) = 18.124

总结

在计算矩阵相乘的时候,我使用的是2000*2000的矩阵来进行计算,计算量是与矩阵的维度成二次方增长的,可以看到,串行时,计算时间很长,而当只有一对主从节点时,从节点计算,主节点汇总,意外发现计算时间竟然比串行程序还稍稍短一点。

从并行的角度看,我们明显可以看出,并行节点数越多,时间开销越小。从每增加2倍节点数时计算的加速比可以看出,随着并行度的提高,加速比逐渐远离理论加速比数值。因为此时,包括节点间通信在内的其他操作造成的时间开销占比变得越来越大,使得每增加2倍节点时产生的加速比效果越来越不明显。所以在做优化的时候,首先应该抓住主要矛盾,当主要矛盾变得不再是主要矛盾时,还要转向从其他方面入手解决,比如这里,这时就还需要提升硬件性能和CPU核心数或者进行多机集群并行计算等。

版权声明本博客的文章除特别说明外均为原创,本人版权所有。欢迎转载,转载请注明作者及来源链接,谢谢。本文地址: https://blog.ailemon.net/2018/06/19/using-c-language-by-mpi-to-parallel-compute-matrix-mutiply/ All articles are under Attribution-NonCommercial-ShareAlike 4.0

关注“AI柠檬博客”微信公众号,及时获取你最需要的干货。

format,png

format,png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值