矩阵乘法MPI并行程序报告记录.docx
《矩阵乘法MPI并行程序报告记录.docx》由会员分享,可在线阅读,更多相关《矩阵乘法MPI并行程序报告记录.docx(16页珍藏版)》请在冰豆网上搜索。
矩阵乘法MPI并行程序报告记录
————————————————————————————————作者:
————————————————————————————————日期:
1.实验目的
1.1掌握集群的使用方法。
1.2掌握以并行的方式分析问题、设计并行程序的方法。
1.3掌握如何对并行程序进行简单的性能分析
2.实验要求
1
2
2.1使用MPI、OpenMp等并行程序设计方法设计矩阵乘法的并行程序。
2.2随机产生所需的矩阵元素,数据项不得少于1000*1000。
2.3尽量设计较高的加速比
3.实验环境
3
3.1硬件环境:
两个集群节点blade13、blade15。
3.2软件环境:
Linux、gcc、Win7、VC++6.0。
3.3连接方式:
XmanagerEnterprise4.0远程桌面连接211.69.198.203。
4.实验程序
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
{
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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_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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析分析公式:加速比=串行执行时间/并行执行时间效率=加速比/节点数123455.1串行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次11.00099.000372.000799.000第2次11.00098.000376.000799.000第3次11.00099.000373.000802.000平均值11.00098.667373.667800.000图1.不同矩阵规模下串行程序的执行时间5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)矩阵规模实验结果1000200030004000第1次0.8296.83323.37255.422第2次0.8326.83823.31555.786第3次0.8376.82023.56055.732平均值0.8336.83023.41655.647加速比13.20514.44615.95814.376效率1.6511.8061.9951.797图2.八个节点时,不同矩阵规模下并行程序的执行时间图3.八个节点时,不同矩阵规模下并行程序的加速比图4.八个节点时,不同矩阵规模下并行程序的效率分析随着矩阵规模的增加执行时间程序的执行时间急剧增加。加速比程序的加速比基本保持不变。效率程序的效率基本保持不变。5.3矩阵规模为1000*1000时,不同节点数下并行程序的执行时间(秒)节点个数实验结果2345678第1次5.7922.9171.9451.4551.1700.9730.829第2次5.7932.8991.9441.4741.1670.9710.832第3次5.8382.9151.9351.4531.1690.9750.837平均值5.8082.9101.9411.4611.1690.9730.833加速比1.9843.7805.6677.2599.41011.30513.205效率0.9921.2601.4171.4521.5681.6151.651图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比图7.矩阵规模1000*1000时,不同节点下的并行程序的效率分析随着计算节点数的增加,执行时间程序的执行时间迅速减少,然后趋于平稳。加速比程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。效率程序的效率逐步增长,然后趋于平缓。6.用户手册1234566.1连接:sshpppusr@211.69.168.203密码:******6.2登陆:sudosshblade13或者blade156.3切换至工作目录:cd/home/pppusr/*****6.4编译:mpicc-omatrixmatrix_multi.c6.5运行:mpirun-np8./matrix#include#include#include#defineN1000#defineFROM_MASTER1#defineFROM_SLAVE2intA[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;}
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.性能分析
分析公式:
加速比=串行执行时间/并行执行时间
效率=加速比/节点数
5
5.1串行程序的执行时间(秒)
矩阵规模
实验结果
1000
2000
3000
4000
第1次
11.000
99.000
372.000
799.000
第2次
98.000
376.000
第3次
373.000
802.000
平均值
98.667
373.667
800.000
图1.不同矩阵规模下串行程序的执行时间
5.2八个节点时,不同矩阵规模并行程序的执行时间(秒)
0.829
6.833
23.372
55.422
0.832
6.838
23.315
55.786
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时,不同节点数下并行程序的执行时间(秒)
节点个数
6
7
8
5.792
2.917
1.945
1.455
1.170
0.973
5.793
2.899
1.944
1.474
1.167
0.971
5.838
2.915
1.935
1.453
1.169
0.975
5.808
2.910
1.941
1.461
1.984
3.780
5.667
7.259
9.410
11.305
0.992
1.260
1.417
1.452
1.568
1.615
图5.矩阵规模1000*1000时,不同节点下的并行程序的执行时间
图6.矩阵规模1000*1000时,不同节点下的并行程序的加速比
图7.矩阵规模1000*1000时,不同节点下的并行程序的效率
随着计算节点数的增加,
程序的执行时间迅速减少,然后趋于平稳。
程序的加速比基本呈线性增长,公式趋y=1.8374x+0.022。
程序的效率逐步增长,然后趋于平缓。
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
#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;
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;}
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;}
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;
//有的程序是将时间函数放在这个for循环的两边
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;}
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;}
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;}
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,标志并行代码段的结束*/
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1