矩阵乘法MPI并行程序报告记录.docx

上传人:b****3 文档编号:10337582 上传时间:2023-05-25 格式:DOCX 页数:16 大小:171.04KB
下载 相关 举报
矩阵乘法MPI并行程序报告记录.docx_第1页
第1页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第2页
第2页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第3页
第3页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第4页
第4页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第5页
第5页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第6页
第6页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第7页
第7页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第8页
第8页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第9页
第9页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第10页
第10页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第11页
第11页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第12页
第12页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第13页
第13页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第14页
第14页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第15页
第15页 / 共16页
矩阵乘法MPI并行程序报告记录.docx_第16页
第16页 / 共16页
亲,该文档总共16页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

矩阵乘法MPI并行程序报告记录.docx

《矩阵乘法MPI并行程序报告记录.docx》由会员分享,可在线阅读,更多相关《矩阵乘法MPI并行程序报告记录.docx(16页珍藏版)》请在冰点文库上搜索。

矩阵乘法MPI并行程序报告记录.docx

矩阵乘法MPI并行程序报告记录

矩阵乘法MPI并行程序报告记录

 

 

————————————————————————————————作者:

————————————————————————————————日期:

 

1.实验目的

1.1掌握集群的使用方法。

1.2掌握以并行的方式分析问题、设计并行程序的方法。

1.3掌握如何对并行程序进行简单的性能分析

2.实验要求

1

2

2.1使用MPI、OpenMp等并行程序设计方法设计矩阵乘法的并行程序。

2.2随机产生所需的矩阵元素,数据项不得少于1000*1000。

2.3尽量设计较高的加速比

3.实验环境

1

2

3

3.1硬件环境:

两个集群节点blade13、blade15。

3.2软件环境:

Linux、gcc、Win7、VC++6.0。

3.3连接方式:

XmanagerEnterprise4.0远程桌面连接211.69.198.203。

4.实验程序

1

2

3

4

4.1随机算法产生矩阵:

srand((unsignedint)time(NULL));

for(i=0;i

{

for(j=0;j

{

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

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

C[i][k]=0;

}

}

4.2串行程序设计

time(&start);

for(i=0;i

{

for(k=0;k

{

C[i][k]=0;

for(j=0;j

{

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

}

}

}

time(&end);

4.3并行程序设计

MPI_Init(&argc,&argv)

MPI_Finalize()

MPI_Init用来初始化MPI执行环境,建立多个MPI进程之间的联系,为后续通信做准备。

而MPI_Finalize则是结束MPI执行环境。

这两个函数就是定义MPI程序的并行区的,除了检测是否初始化的函数之外,不应该在这两个函数定义的区域外调用其它MPI函数。

这两个函数都返回整型值,标识函数是否调用成功。

 

intMPI_Comm_rank(MPI_Commcomm,int*rank)

MPI_Comm_rank函数用来标识各个MPI进程,获取调用该函数进程的进程号,将自身与其他进程区分。

MPI_Comm_rank返回整型的错误值,需要提供两个参数:

MPI_Comm类型的通信域,标识参与计算的MPI进程组。

上面例子中使用的是MPI_COMM_WORLD,这个进程组是MPI实现预先定义好的进程组,指的是所有MPI进程所在的进程组。

如果想要申请自己的特殊的进程组,则需要通过MPI_Comm定义并通过其它MPI函数生成。

&rank返回调用进程中的标识号。

intMPI_Comm_size(MPI_Commcomm,int*size)

MPI_Comm_size函数用来获取指定通信域的进程个数,确定自身需要完成的任务数。

MPI_Comm类型的通信域,标识参与计算的MPI进程组。

上面的例子中用的是MPI_COMM_WORLD。

&size返回相应进程组中的进程数。

intMPI_Send(void*buf,intcount,MPI_Datatypedatatype,intdest,inttag,MPI_Commcomm)

MPI_Send函数用于发送一个消息到目标进程。

通信域中的dest进程发送数据,数据存放在buf中,类型是datatype,个数是count,这个消息的标志是tag,用以和本进程向同一目的进程发送的其它消息区别开来。

intMPI_Recv(void*buf,intcount,MPI_Datatypedatatype,intsource,inttag,MPI_Commcomm,MPI_Status*status)

MPI_Recv函数用于从指定进程接收一个消息。

它的含义是进程从comm域中source进程接收标签号为tag的数据,并保存到buf中。

接收缓冲区buf的大小不能小于发送过来的消息的长度。

否则会由于数组越界导致程序出错。

主进程

if(process_id==0)

{

row_aver=N/slave_num;

remainder=N%slave_num;

offset=0;

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

{

rows=(dest<=remainder)?

row_aver+1:

row_aver;

printf("sending%drowstoprocess%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;

}

start_time=MPI_Wtime();

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();

printf("processcost%fseconds\n",end_time-start_time);

}

从进程

if(process_id>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);

//矩阵乘法

for(i=0;i

{

for(k=0;k

{

inttmp=A[i][k];

for(j=0;j

{

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

}

}

}

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);

}

MPI_Finalize();

return0;

}

5.性能分析

分析公式:

加速比=串行执行时间/并行执行时间

效率=加速比/节点数

1

2

3

4

5

5.1串行程序的执行时间(秒)

矩阵规模

实验结果

1000

2000

3000

4000

第1次

11.000

99.000

372.000

799.000

第2次

11.000

98.000

376.000

799.000

第3次

11.000

99.000

373.000

802.000

平均值

11.000

98.667

373.667

800.000

图1.不同矩阵规模下串行程序的执行时间

5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)

矩阵规模

实验结果

1000

2000

3000

4000

第1次

0.829

6.833

23.372

55.422

第2次

0.832

6.838

23.315

55.786

第3次

0.837

6.820

23.560

55.732

平均值

0.833

6.830

23.416

55.647

加速比

13.205

14.446

15.958

14.376

效率

1.651

1.806

1.995

1.797

图2.八个节点时,不同矩阵规模下并行程序的执行时间

图3.八个节点时,不同矩阵规模下并行程序的加速比

图4.八个节点时,不同矩阵规模下并行程序的效率

分析

随着矩阵规模的增加

执行时间

程序的执行时间急剧增加。

加速比

程序的加速比基本保持不变。

效率

程序的效率基本保持不变。

5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)

节点个数

实验结果

2

3

4

5

6

7

8

第1次

5.792

2.917

1.945

1.455

1.170

0.973

0.829

第2次

5.793

2.899

1.944

1.474

1.167

0.971

0.832

第3次

5.838

2.915

1.935

1.453

1.169

0.975

0.837

平均值

5.808

2.910

1.941

1.461

1.169

0.973

0.833

加速比

1.984

3.780

5.667

7.259

9.410

11.305

13.205

效率

0.992

1.260

1.417

1.452

1.568

1.615

1.651

图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间

图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比

图7.矩阵规模1000*1000时,不同节点下的并行程序的效率

分析

随着计算节点数的增加,

执行时间

程序的执行时间迅速减少,然后趋于平稳。

加速比

程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。

效率

程序的效率逐步增长,然后趋于平缓。

6.用户手册

1

2

3

4

5

6

6.1连接:

sshpppusr@211.69.168.203密码:

******

6.2登陆:

sudosshblade13或者blade15

6.3切换至工作目录:

cd/home/pppusr/*****

6.4编译:

mpicc-omatrixmatrix_multi.c

6.5运行:

mpirun-np8./matrix

#include

#include

#include

#defineN1000

#defineFROM_MASTER1

#defineFROM_SLAVE2

intA[N][N],B[N][N];

unsignedlonglongC[N][N];

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

intmain(intargc,char**argv)

{

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

intprocess_id;

intslave_num;

intdest;//目的进程标识号

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

introws;

introw_aver;

intremainder;

intoffset;//行偏移量

inti,j,k;

doublestart_time,end_time;

srand((unsignedint)time(NULL));

for(i=0;i

{

for(j=0;j

{

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

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

C[i][k]=0;

}

}

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)

{

row_aver=N/slave_num;

remainder=N%slave_num;

offset=0;

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

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

{

rows=(dest<=remainder)?

row_aver+1:

row_aver;

printf("sending%drowstoprocess%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;

}

start_time=MPI_Wtime();

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();

printf("processcost%fseconds\n",end_time-start_time);

}

if(process_id>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);

for(i=0;i

{

for(k=0;k

{

inttmp=A[i][k];

for(j=0;j

{

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

}

}

}

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);//将计算得到的值发回主进程

}

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

MPI_Finalize();

return0;

}

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2