nullnull计算流体力学讲义2011
第13讲 MPI并行程序
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
(1)
李新亮
lixl@imech.ac.cn ;力学所主楼219; 82543801
知识点:
MPI程序的运行机制—— 拷贝N份,给每个进程一份
MPI的基本库函数——6个库函数
“对等式”编程方法—— 站在单个进程的角度思考
*讲义、课件上传至 www.cfluid.com (流体中文网) -> “流体论坛” ->“ CFD基础理论 ”Copyright by Li XinliangnullCopyright by Li Xinliang*Part 1: 基本概念及MPI并行编程入门 并行计算基本概念
MPI并行编程入门
1)MPI 简介
2)MPI 的基本函数及消息传递
3)MPI的常用函数
4)对等式编程思想的举例说明 ——全收集、矩阵相乘null一、 基本概念并行计算机简介
大规模并行计算——超级计算(Supercomputing)/高性能计算 (HPC)
为何需要超级计算?
应用领域的巨大计算需求
单CPU的计算能力有限
应用对计算资源的需求*Copyright by Li Xinliangnull ● CFD的计算资源依赖性
计算量大——流动的多尺度性 (湍流)
大飞机全部流动细节完全分辨:
最小尺度:mm~ mm 量级;
计算网格: 1012-1016 ; 需计算量: ~ 1020-30 ;
工程需求: 8个小时之内完成计算
预计: LES: 2045年; DNS: 2080年最大尺度~ mmmnull几种我们常见的并行计算机并行计算机体系结构*Copyright by Li Xinliang内存带宽瓶颈访存冲突机制控制复杂虚拟共享存储“NUMA”
访存冲突机制控制复杂
克服了访存冲突及内存瓶颈访存的局部性—— 对网络要求不严null*Copyright by Li Xinliang低价格
可扩展null自行搭建的简易机群*Copyright by Li Xinliang并行机群: 搭建简单 —— 简单的局域网并行机群 = 局域网早期 作者搭建的简易机群机群软件:Linux/Windows; 套件OSCAR; MPICH.NT, ……我国最早搭建的机群: LSEC 张林波 搭建的32节点机nullCopyright by Li Xinliang*美洲虎/1700万亿次曙光5000A/160万亿次天河1号 千万亿次
CPU+GPU混合系统联想深腾7000/106万亿次单精度千万亿次的
GPU系统 Mole-xx顶级的超级计算机目标: 每秒 1摩尔次浮点运算
(1 mole=6.02*1023)
nullCopyright by Li Xinliang*Top5 超级计算机 (2010-11)null2. 并行程序设计工具
1) 共享存储式
自动并行(并行编译器)
Intel Fortran/C 编译器
ifc aa.for -parallel
编译目标: 多线程程序
OpenMP编译指示符: !omp parallel …… *Copyright by Li XinliangnullCluster 系统2) 分布存储式
HPF (High-Performance Fortran)
基于数据并行,程序改动较小
效率较低
PVM (Parallel Virtual Machine)
MPI (Message Passing Interface)
基于消息传递
效率较高*Copyright by Li Xinliangnull MPI 的编译、运行环境
1) 并行计算机 (力学所机群、深腾7000,曙光5000A)
编译: mpif90 / mpif77/mpicc
f90/f77 -I/usr/local/mpi/include
-L/usr/local/mpi/lib -lm -lmpi
运行: mpirun / bsub
2)MPI for NT (Windows 2000,XP)
编译环境:Visual Fortran/ MS Develop Studio
设置:头文件路径、连接路径
运行: mpirun二、MPI并行编程入门
1. 简介*Copyright by Li XinliangnullCopyright by Li Xinliang*设置Windows下的MPI环境 Step 1: 下载并安装mpich.nt.1.2.5安装包;
Step 2: 更改Visual Fortran的环境设置,添加MPICH的 include 及lib 路径
1) Tools->options-> Directories;
在“show directories for:” 栏目选择 “include files”; 在“Directories:” 下的对话框里面添加MPICH include 的路径,例如
“C:/Porgram files/mpich/SDK/include” (根据安装的具体位置而定)
在“show directories for:” 的栏目选择“Library files”, 在 “Directories:”下的对话框里面添加 MPICH Lib 的路径, 例如 “C:/Porgram files/mpich/SDK/lib”
2) 程序编译时,请把mpich.lib 添加在链接库里。
project->settings->link ; 在 objcet/Library modules 下的对话框里面添加 mpich.lib (例如 “kernel32.lib” 变更为 “kernel32.lib ; mpich.lib”)
Step3: 编译程序,生成可执行文件nullCopyright by Li Xinliang*
Step 4: 利用mpirun 运行程序。 (该工具在Windows桌面的 “开始-> 程序-> mpich -> mpd -> MPIRun”)
在“Application:”对话框里面选择刚编译生成的可执行文件。 在“Number of Processes” 对话框里面选择运行运行的进程数 (即所谓“CPU个数”)。 在“Advanced options ” 对话框里面选择 “Always prompt for password ”。 MPIRun 运行时会要求用户输入计算机的用户名和密码。 点击“Run”即可运行 (需要输入计算机的用户名和密码)。注意:
如果程序为f90 程序,请修改mpif.h 。将行首的注释符 “C” 替换为“!”, 否则编译会出错。(mpif.h 在安装路径的include 目录下,通常在 C:/Porgram files/mpich/SDK/include里面)
通常MPIRun需要以计算机管理员的身份运行,需要对计算机设置用户名和密码。如果计算机没有设置密码,则需要在控制面板中设置。
些防火墙及杀毒软件会阻止MPIRun的运行,如出现问题需要关闭这些防火墙及杀毒软件。
nullMPI 程序的运行原理:
服务器(前端机)编译
可执行代码复制 N 份,每个节点运行一份
调用MPI库函数 得到每个节点号 my_id
根据my_id 不同,程序执行情况不同
调用MPI 库函数进行通讯MPI 编程的基本思想: 主从式,对等式重要!*Copyright by Li Xinliang重点:对等式程序设计nullCopyright by Li Xinliang*a.exe对等式
设计“对等式”程序设计思想如果我是其中一个进程;
我应当做……
完成我需要完成的任务站在其中一个进程的角度思考null 一个简单的MPI程序 hello.f90
include ‘mpif.h’
integer myid,numprocs,ierr
call MPI_Init(ierr)
call MPI_Comm_rank(MPI_COMM_WORLD,myid,ierr)
call MPI_Comm_size(MPI_COMM_WORLD,numprocs,ierr)
Print*, "Hello World! my id is :",myid
! 添加自己的程序 ……
!
call MPI_Finalize(ierr)
end*Copyright by Li Xinliangnull运行结果: *Copyright by Li Xinliangnull基本MPI函数 (MPI 子集)
1) MPI初始化
call MPI_Init(ierr)
(out) Integer:: ierr
2) MPI结束
call MPI_Finalize(ierr)
(out) Integer:: ierr*Copyright by Li Xinliang(in) : 输入参数;
(out) : 输出参数; 整数,返回值非0表示出错null3) 得到当前进程标识
call MPI_Comm_rank(MPI_COMM_WORLD,myid,ierr)
(In) Integer:: MPI_COMM_WORLD 为进程所在的通信域
(Out) Integer:: myid, ierr
4) 得到通信域包含的进程数
Call MPI_Comm_size(MPI_COMM_WORLD,numprocs,ierr)
(In) Integer:: MPI_COMM_WORLD
(Out) Integer:: numprocs,ierr
*Copyright by Li Xinliang进程的ID号 (从0开始)
最重要的参数!MPI_COMM_WORLD: MPI预定义的通信域; 可换成创建的通信域Commnull基本概念: 通信域(通信子)01234567891011012345012345MPI_COMM_WORLDMPI_Comm_1MPI_Comm_2*Copyright by Li Xinliang把全班分成几个组,执行任务更方便“班名”, 包含全班同学
MPI 预定义my_id “学号”组的名字(编号)组内编号null MPI 消息传递函数
消息发送
MPI_Send(buf,count,datatype,dest,tag,comm, ierr)
MPI消息:数据描述+信封
数据描述:<起始地址,数据个数,数据类型>
信封: <源/目,标签,通信域>*Copyright by Li Xinliangnull buf: 数据起始地址 (Fortran : 变量名, C: 变量地址/指针)
count: 数据数目 (以datatype为单位,必须连续)
MPI_Send(buf,count,datatype,dest,tag,comm,ierr)*Copyright by Li XinliangDatatype: 数据类型
MPI_INTEGER, MPI_REAL, MPI_DOUBLE_PRECISION, MPI_COMPLEX, MPI_LOGICAL, MPI_CHARACTER, MPI_BYTE, MPI_PACKEDReal*8 x(10)
…… (给x 赋值)
Call MPI_send( x(1), 10, MPI_double_precision, …..)数据的首地址 (不是变量的值)10 个数据(不是10个字节)Fortran : 按地址传送 : x(1) 或 x
C: 按值传送: & (x[0]) 或xnull dest: 发送目标的ID (integer)
Tag: 消息标签 (integer)
Comm: 通信域 (integer), 例:MPI_COMM_WORLD
ierr : 整数,如果成功返回0
例:
real A
…….
if(my_id .eq. 0) Call MPI_Send(A, 1,MPI_REAL,1,27,MPI_COMM_WORLD,ierr)
标签 tag 的作用: 区别不同的消息MPI_Send(buf,count,datatype,dest,tag,comm,ierr)*Copyright by Li Xinliang标签。 举例:0号同学向1号同学发出3封信,内容分别为3科考试成绩, 为了防止弄混,必须用约定的数做标签。目的IDnull消息接收
MPI_Recv(buf,count,datatype,source,tag,comm,status,ierr)
参数: 数据起始地址,数据,类型,源地址,标签,
通信域,返回状态
integer status(MPI_STATUS_SIZE)
MPI接收匹配:数据类型、源地址、标签要一致;否则不接收
*Copyright by Li Xinliang返回状态和调试信息nullMPI_Recv(buf,count,datatype,source,tag,comm,status,ierr)
源地址,标签 *Copyright by Li Xinliang integer status(MPI_STATUS_SIZE) 任意源地址: MPI_ANY_SOURCE
任意标签 :MPI_ANY_TAG包含必要信息MPI预定义的常量
可匹配任意源、任意标签
“无论谁来的信都接收”status(MPI_SOURCE) : 消息的源地址
status(MPI_TAT) : 消息的标签
status(MPI_ERROR) : 错误码nullMPI的消息发送机制—— 两步进行
MPI_Send( A, … ) 发送
MPI_Recv( B, … ) 接收
发送 变量A接收 到变量B配合使用发/收 两步机制;
避免直接读写对方内存;
保证安全性null! sum from 1 to 100 ! Run only for np=2 !!!!
include 'mpif.h'
integer myid,sum_local,sum_local1,sum_global,ierr,status(MPI_STATUS_SIZE)
call MPI_Init(ierr)
call MPI_Comm_Rank(MPI_COMM_WORLD,myid,ierr)
sum_local=0
do i=myid+1,100,2
sum_local=sum_local+i
enddo
print*, 'myid=',myid, ' sum_local=',sum_local
if(myid .eq. 1) call MPI_SEND(sum_local,1,MPI_INTEGER,0,99,MPI_COMM_WORLD,ierr)
if(myid .eq. 0) then
call MPI_RECV(sum_local1,1,MPI_INTEGER,1,99,MPI_COMM_WORLD,status,ierr)
sum_global=sum_local+sum_local1
print*, 'sum_global= ',sum_global
endif
call MPI_Finalize(ierr)
end例: 计算 1+2+3 …+100, 采用2个CPU并行CPU 0: 1+3+5+7 …+99
CPU 1: 2+4+6+8 …+100“对等式”编程思路:
站在每个进程的角度思考*Copyright by Li Xinliangnull结果显示*Copyright by Li Xinliangnull消息传递时要注意防止死锁缓冲区
(“信箱”)MPI_SendMPI_RecvMPI_Send, MPI_Recv 属于
阻塞式发送/接收
发送成功
接收成功发送和接收是两个独立过程 子程序返回Send 与 Recv 一定要配合好发送到“信箱”即为成功*Copyright by Li Xinliang重要!null发生死锁的情况只发送,不接收只接收,不发送*Copyright by Li Xinliangnull例,任务: 进程0 发送变量 A给进程1
进程1发送变量 B给进程0 if (myid .eq. 0) then
call MPI_send(A,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
call MPI_recv(B,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
Else if(myid .eq. 1) then
call MPI_recv(A,1,MPI_real, 0, 99,MPI_Comm_World,ierr )
call MPI_send(B,1,MPI_real, 0, 99,MPI_Comm_World,ierr)
endif
Step 1Step 2Step 1Step 2不会死锁*Copyright by Li Xinliangnull死锁的例子 if (myid .eq. 0) then
call MPI_recv(B,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
call MPI_send(A,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
Else if(myid .eq. 1) then
call MPI_recv(A,1,MPI_real, 0, 99,MPI_Comm_World,ierr )
call MPI_send(B,1,MPI_real, 0, 99,MPI_Comm_World,ierr)
endif
Step 1Step 1Step 2不会死锁??Step 2*Copyright by Li Xinliangnull有可能死锁的例子 if (myid .eq. 0) then
call MPI_send(A,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
call MPI_recv(B,1,MPI_real, 1, 99,MPI_Comm_World,ierr)
Else if(myid .eq. 1) then
call MPI_send(B,1,MPI_real, 0, 99,MPI_Comm_World,ierr)
call MPI_recv(A,1,MPI_real, 0, 99,MPI_Comm_World,ierr )
endif
Step 2Step 2Step 1*Copyright by Li Xinliangnull使用MPI_Sendrecv( ) 函数来避免死锁MPI_SendRecv(buf1,count1,datatype1,dest,tag1,
buf2,count2,datatype2,source,tag2,comm,status,ierr)
= MPI_Send(buf1,count1,datatype1,dest,tag1, comm,ierr)
+MPI_Recv(buf2,count2,datatype2,source,tag2,comm,status,ierr)
次序由系统决定
*Copyright by Li Xinliangnull基本的MPI函数(6个)—— MPI的子集
MPI初始化 MPI_Init(ierr) ; MPI结束 MPI_Finalize(ierr)
得到当前进程标识
MPI_Comm_rank(MPI_COMM_WORLD,myid,ierr)
得到通信域包含的进程数
MPI_Comm_size(MPI_COMM_WORLD,numprocs,ierr)
消息发送
MPI_Send(buf,count,datatype,dest,tag,comm, ierr)
消息接收
MPI_Recv(buf,count,datatype,source,tag,comm,status,ierr)
MPI 只能点到点通信,其他函数是用这6个函数开发的;
使用这6个函数,可以实现MPI的全部功能。nullCopyright by Li Xinliang*系统时间(墙钟)函数: MPI_Wtime( ) real*8:: time
time=MPI_Wtime( )
返回当前墙钟时间 (单位:秒)
(Wall time 与CPU time不同,Wall time更可靠)CPU分时为所有进程服务Real *8:: Time_begin, Time_end
… (初始化)
Time_begin=MPI_Wtime( )
….
….(计算任务)
….
Call MPI_Barrier( comm, ierr)
Time_end=MPI_Wtime()
Print*, “计算时间为:” ,Time_end-Time_begin
…可用来测量程序的执行速度
测量加速比及并行效率加速比= N个进程的执行速度/单个进程的执行速度并行效率= 加速比/N作者的Hoam-OpenCFD软件加速比测试CPU Core number 1024-16384并行效率
89.6%通常要进行同步,然后测量时间,否则各进程报出的时间不同。null3. 常用的MPI函数1) 广播 MPI_Bcast(buff,count,datatype,root,comm,ierr)
参数: 数据缓冲区,数目,数据类型,根进程,通讯域
例:
real A
if(myid .eq. 0) then
open(55,file=‘data.dat’)
read(55,*) A
close(55)
endif
call MPI_Bcast(A,1,MPI_REAL,0,MPI_COMM_WORLD,ierr)
广播: 树状传播,效率较高*……广播的逻辑图广播的实际实现方式——树状传播MPI的消息: 只能点到点传递;不能真正“广播”null2) 规约 (求和,求最大值,求最小值,……)
MPI_Reduce(sendbuf,recvbuf,count,datatype,op,root,comm,ierr)
发送缓冲区,接收缓冲区,数目,数据类型,规约操作,通讯域! sum from 1 to 100 ! Run only for np=2 !!!!
include 'mpif.h'
integer myid,sum_local,sum_global,ierr
call MPI_Init(ierr)
call MPI_Comm_Rank(MPI_COMM_WORLD,myid,ierr)
sum_local=0
do i=myid+1,100,2
sum_local=sum_local+i
enddo
call MPI_Reduce(sum_local,sum_global,1,MPI_INTEGER, &
MPI_SUM,0, MPI_COMM_WORLD,ierr)
print*, 'sum_global= ',sum_global
call MPI_Finalize(ierr)
end*Copyright by Li Xinliangnull预定义的规约操作:
MPI_MAX 最大值 MPI_LXOR 逻辑异或
MPI_MIN 最小值 MPI_BXOR 按位异或
MPI_SUM 求和 MPI_MAXLOC 最大值及位置
MPI_PROD 求积 MPI_MINLOC 最小值及位置
MPI_LAND 逻辑与
MPI_BAKD 按位与
MPI_LOR 逻辑或
MPI_BOR 安位或*Copyright by Li Xinliangnull同步 MPI_Barrier ( comm, ierr)
Comm: 通讯域, ierr: 返回值 等待所有进程都调用MPI_Barrier(), 函数才能返回……
……
Call MPI_barrier(MPI_COMM_WORLD, ierr)
……*Copyright by Li XinliangnullCopyright by Li Xinliang*4. “对等式”编程示例例1:全收集的实现 MPI_Allgather( )题目: N个进程, 每个进程有一个数A; 把所有进程的数收集起来,按进程号的次序形成数组A0 (1:N), 存放到所有进程中。 把每个同学的电话号码收集起来,形成通讯录,发给全班同学也可以是数组,各进程的数A可以不同 A0(1) A0(2) A0(3) A0(4)null方式1: 根进程收集所有数据; 根进程发送到所有进程
if(myid .eq. 0) then
A0(0)=A
do id=1,Nproc-1
call MPI_Recv(A0(id),1,MPI_Real,id,99,MPI_Comm_World,status,ierr)
enddo
else
call MPI_Send(A,1,MPI_Real,0,…...)
endif
if(myid .eq. 0) then
do id=1,Nproc-1
call MPI_Send(A0,Nproc,MPI_Real,id, ……)
enddo
else
call MPI_Recv(A0,Nproc,MPI_Real,0,……)
endif*Copyright by Li Xinliang“班长”依次与所有同学通信,收集信息;
收集后依次通信,发放信息负载不均衡
效率最低
可能会死锁null方式2: 根进程收集所有数据; 根进程广播到所有进程
if(myid .eq. 0) then
A0(0)=A
do id=1,Nproc-1
call MPI_Recv(A0(id),1,MPI_Real,id,99,MPI_Comm_World,status,ierr)
enddo
else
call MPI_Send(A,1,MPI_Real,0,…...)
endif
call MPI_Bcast(A0,Nproc,MPI_Real,0,MPI_Comm_world,ierr)
效率高于 (1)
是MPI_Allgather( )的原有的标准方式*Copyright by Li Xinliang广播的实现方式“班长”依次收集信息后,“广播”给全班nullCopyright by Li Xinliang*……1A send to ID 0 0 1 2 3 N-1……1B 0 1 2 3 N-1Step 1: “我” (my_id 进程) 向my_id+1进程发数据;
“我” 收my_id-1进程发来的数据该步完成后:
“我” (my_id 进程)得到了my_id-1的数据全收集的实现图解方式3: 循环通信nullCopyright by Li Xinliang*……2A send to ID 1 0 1 2 3 N-1……2B 0 1 2 3 N-1Step 2:
“我”向 my_id+2 进程发数据;
“我”收 my_id-2进程发来的数据;该步完成后: “我”得到了my_id-1, my_id-2进程的数据Step 3 :我向my_id+3发数据, 我收my_id-3发来的数据
……
Step N-1 完成后, 我得到了全部数据;
全体进程也得到了全部数据对等式编程思想:每个人做好自己的工作,全部工作就做好了不设班长,所有人工作量相同null循环通讯: 由张林波研究员首次提出
do step=1,Nproc-1
id_send=myid+step; if(id_send .ge. Nproc) id_send =id_send-Nproc
id_recv=myid-step; if (id_recv .lt. 0) id_recv=id_recv+Nproc
call MPI_Send(A,1,MPI_Real,id_send,99, &
MPI_Comm_World,ierr)
call MPI_Recv(A0(id_recv),1,MPI_Real,id_recv,99, &
MPI_Comm_World,status,ierr)
enddo
效率高于 (1) (2)
是MPI_Allgather( )的现有的标准方式*Copyright by Li Xinliangnull 计算矩阵 A*B=C , A,B,C: N*N 矩阵
采用P个进程计算 (N能被P整除)
存储方式:分布存储, A, C 按行分割, B 按列分割矩阵 A B C*Copyright by Li Xinliang例2. 计算矩阵乘积null“对等式”程序设计思想: 站在每个进程角度思考
“我”的数据: dimension A1(N/P,N), B1(N,N/P), C1(N/P,N)
“我”的任务: 计算 C1
0, …… K…P-1ACBA1B1C1*Copyright by Li Xinliangnull需要得到整个矩阵 B —— 自己只有 Bk—— 向他人索取
do id=0,P-1
call MPI_Recv(B_tmp, N*N/P, MPI_REAL, id, …….. )
…… 计算出 C1(id) …… (C1(id)=A1*B_tmp)
enddo
只索取数据,何人提供?
“命令”他人提供数据? 不符合“对等式”程序设计思想
0, …… K…P-1ACB如何完成任务?B_tmp(N,N/P,P)C1(0)C1(1)C1*Copyright by Li XinliangnullB顺次发送数据
do step=0,P-1
if(myid .eq. step) then
do id=0,P-1
call MPI_Send(B1,N*N/P,MPI_REAL,id, ……)
enddo
endif
call MPI_Recv(B_tmp, N*N/P, MPI_REAL, step, … )
…… 计算出 C1(id) …… (C1(id)=A1*B_tmp)
enddo
问题: 负载不均衡
实际上是 串行
*Copyright by Li Xinliang一窝蜂地涌向同一个进程,负载不均衡,容易阻塞nullB按节拍循环发送数据 (同“全收集”)
do step=0,P-1
id_send=myid+step; if (id_send .ge. P) id_send=id_send-P
id_recv=myid-step; if(id_recv .lt. 0) id_recv=id_recv+P
call MPI_Send(B1,N*N/P,MPI_REAL,id_send, ……)
call MPI_Recv(B_tmp, N*N/P, MPI_REAL, id_recv, … )
…… 计算出 C1(id_recv) …… (C1(id_recv)=A1*B_tmp)
enddo
*Copyright by Li Xinliang…… send to ID 0 0 1 2 3 N-1工作数组,存放接受来的矩阵Bknull 回顾
MPI 程序的运行原理:
服务器(前端机)编译
可执行代码复制 N 份,每个节点运行一份
调用MPI库函数 得到每个节点号 myid
根据myid 不同,程序不同
调用MPI 库函数进行通讯MPI 编程的基本思想: 主从式,对等式推荐采用对等式编程思想null 基本的MPI函数(6个)
MPI初始化 MPI_Init(ierr) ; MPI结束 MPI_Finalize(ierr)
得到当前进程标识
MPI_Comm_rank(MPI_COMM_WORLD,myid,ierr)
得到通信域包含的进程数
MPI_Comm_size(MPI_COMM_WORLD,numprocs,ierr)
消息发送
MPI_Send(buf,count,datatype,dest,tag,comm, ierr)
消息接收
MPI_Recv(buf,count,datatype,source,tag,comm,status,ierr)
nullCopyright by Li Xinliang*作业13.1 熟悉MPI环境及基本编程
1) 建立MPI运行环境 (有并行机账户或在微机上安装MPI环境)。
2) 编制如下基本的MPI程序
计算S=1+2+3……+1000
要求程序可以实现N个进程的并行运行且负载尽量均衡。 N可变,程序中使用MPI_Comm_Size()函数读入N。由0号进程打印计算结果。
3)在并行环境上运行,输出结果。
要求: 提交源程序及运行情况的屏幕截图nullCopyright by Li Xinliang*13.2 实现矩阵相乘的并行计算矩阵A, B 均为N*N的方阵,试计算矩阵C=AB;
使用P个进程并行计算(N可以被P整除);
矩阵A,B及C均采用分布式存储;
A, C按行分割, B按列分割存储(见本稿 47页)。
要求编写计算C矩阵的MPI程序,并进行计算。实际计算时,矩阵A, B请采用如下值, N设为100计算出C矩阵后,请计算 ,并由根节点打印出来。将S值与串行程序的结果进行对比,校验程序的正确性;
使用1,2,4,10个进程进行计算,并利用MPI_Wtime( )函数计算程序的运行时间;考核加速比及计算效率。要求: 1)提交计算程序; 2)使用1,2,4,10个进程计算,提交计算结果(S值及计算时间)、计算效率及加速比。