C 中使用 MPI 只需要一行头文件:
编译方式 不用 gcc,用 mpicc:
1 mpicc program.c -o program
运行方式
最简示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <mpi.h> #include <stdio.h> int main (int argc, char *argv[]) { MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); printf ("Hello from process %d\n" , rank); MPI_Finalize(); return 0 ; }
编译运行:
1 2 mpicc hello.c -o hello mpirun -np 4 ./hello
MPI_Comm_size 和 MPI_Comm_rank 简单解释
函数
作用
MPI_Comm_size
获取总共有多少个进程
MPI_Comm_rank
获取我是第几个进程
举例说明 假设运行 mpirun -np 4 ./program(4个进程):
1 2 MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank(MPI_COMM_WORLD, &rank);
进程
numprocs
rank
进程0
4
0
进程1
4
1
进程2
4
2
进程3
4
3
用途 1 2 3 4 5 6 7 if (rank == 0 ) { printf ("我是老大\n" ); } else { printf ("我是worker %d\n" , rank); }
rank 用来区分不同进程,让它们干不同的活。
MPI_COMM_WORLD 一句话解释 包含所有进程的”群聊”
类比理解 把它想象成微信群:
概念
类比
MPI_COMM_WORLD
包含所有人的大群
rank
你在群里的编号
size
群里总人数
为什么需要它 MPI 的所有通信都要指定”在哪个群里”:
1 2 3 4 5 6 MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Send(data, ..., MPI_COMM_WORLD);
简单理解 90%的情况下,直接用 MPI_COMM_WORLD 就行 ,不用想太多。
它就是”所有进程”的意思。
MPI_Barrier 一句话解释 让所有进程在这里等,等齐了再一起往下走
类比 就像旅游团的集合点:
跑得快的人先到,必须等
跑得慢的人后到
全到齐了 ,大家再一起出发
例子 1 2 3 4 5 printf ("进程 %d 到达\n" , rank); MPI_Barrier(MPI_COMM_WORLD); printf ("进程 %d 继续执行\n" , rank);
输出:
1 2 3 4 5 6 7 8 进程 2 到达 进程 0 到达 进程 1 到达 进程 3 到达 (等所有人都到达后) 进程 0 继续执行 进程 1 继续执行 ...
常见用途
MPI_Bcast 一句话解释 一个进程把数据广播给所有其他进程
类比 群主发群公告,所有人都收到同样的内容。
例子 1 2 3 4 5 6 7 8 9 10 int data;if (rank == 0 ) { data = 100 ; } MPI_Bcast(&data, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
参数解释 1 2 MPI_Bcast(&data, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
参数
含义
&data
要广播的数据
1
数据个数
MPI_INT
数据类型
0
谁是发送者 (root进程)
MPI_COMM_WORLD
通信范围
图示 1 2 3 4 5 广播前: 广播后:进程0: 100 进程0: 100 进程1: ? --> 进程1: 100 进程2: ? 进程2: 100 进程3: ? 进程3: 100
MPI_Scatter 一句话解释 一个进程把数据切开,分别发给每个进程
类比 发牌:一副牌分给每个玩家,每人拿一部分。
图示 1 2 3 4 5 分发前: 分发后:进程0: [A,B,C,D] 进程0: A 进程1: ? --> 进程1: B 进程2: ? 进程2: C 进程3: ? 进程3: D
例子 1 2 3 4 5 6 7 8 int send_buf[4 ] = {10 , 20 , 30 , 40 }; int recv_data; MPI_Scatter(send_buf, 1 , MPI_INT, &recv_data, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
对比 Bcast
操作
效果
Bcast
所有人收到相同 数据
Scatter
每人收到不同 的一块
MPI_Gather 一句话解释 每个进程把数据交给一个进程,汇总到一起
类比 收作业:老师把每个学生的作业收上来,汇成一摞。
图示 1 2 3 4 5 收集前: 收集后:进程0: A 进程0: [A,B,C,D] 进程1: B --> 进程1: ? 进程2: C 进程2: ? 进程3: D 进程3: ?
例子 1 2 3 4 5 6 7 8 int send_data = rank * 10 ; int recv_buf[4 ]; MPI_Gather(&send_data, 1 , MPI_INT, recv_buf, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
对比
操作
方向
效果
Scatter
1 → 多
切开分发
Gather
多 → 1
汇总收集
记忆 Scatter 和 Gather 是反操作
MPI_Allgather 一句话解释 每个进程都收集到所有人的数据
类比 交换名片:每人把自己的名片发给所有人,最后人人都有一套完整名片。
图示 1 2 3 4 5 收集前: 收集后: 进程0 : A 进程0 : [ A , B , C , D ] 进程1 : B --> 进程1 : [ A , B , C , D ] 进程2 : C 进程2 : [ A , B , C , D ] 进程3 : D 进程3 : [ A , B , C , D ]
例子 1 2 3 4 5 6 7 8 int send_data = rank * 10 ; int recv_buf[4 ]; MPI_Allgather(&send_data, 1 , MPI_INT, recv_buf, 1 , MPI_INT, MPI_COMM_WORLD);
对比
操作
结果在谁那
Gather
只有root有完整数据
Allgather
人人都有 完整数据
记忆 1 2 Gather = 收集到一人Allgather = 收集到所有人
All = 所有人都得到结果
MPI_Alltoall 一句话解释 每个进程给每个进程发一份数据,全员互换
类比 交换礼物:每人准备n份礼物,分别送给每个人,同时也收到每人送来的礼物。
图示 1 2 3 4 5 交换前: 交换后: 进程0 : [A0,A1 ,A2 ,A3 ] 进程0 : [A0,B0,C0 ,D0 ] 进程1 : [B0,B1,B2,B3] --> 进程1 : [A1 ,B1,C1 ,D1 ] 进程2 : [C0 ,C1 ,C2 ,C3 ] 进程2 : [A2 ,B2,C2 ,D2 ] 进程3 : [D0 ,D1 ,D2 ,D3 ] 进程3 : [A3 ,B3,C3 ,D3 ]
每人的第i份 → 发给进程i
例子 1 2 3 4 5 6 7 8 9 int send_buf[4 ] = {rank*10 , rank*10 +1 , rank*10 +2 , rank*10 +3 };int recv_buf[4 ]; MPI_Alltoall(send_buf, 1 , MPI_INT, recv_buf, 1 , MPI_INT, MPI_COMM_WORLD);
对比
操作
模式
Gather
多 → 1
Allgather
多 → 所有
Alltoall
所有 ↔ 所有
记忆
MPI_Reduce 一句话解释 每个进程的数据做运算,结果汇总到一个进程
类比 统计总分:每人报自己的分数,老师加起来得到总分。
图示 1 2 3 4 5 规约前: 规约后(求和): 进程0: 1 进程0: 10 (=1+2+3+4) 进程1: 2 --> 进程1: ? 进程2: 3 进程2: ? 进程3: 4 进程3: ?
例子 1 2 3 4 5 6 7 8 int my_data = rank + 1 ; int result; MPI_Reduce(&my_data, &result, 1 , MPI_INT, MPI_SUM, 0 , MPI_COMM_WORLD);
常用操作
操作
含义
MPI_SUM
求和
MPI_MAX
最大值
MPI_MIN
最小值
MPI_PROD
乘积
对比
操作
作用
Gather
收集数据(不运算)
Reduce
收集并运算
记忆 1 2 Gather = 收作业Reduce = 收作业 + 算总分
MPI_Allreduce 一句话解释 每个进程的数据做运算,结果人人都有
类比 统计总分:每人报分数,加起来后,每人都知道 总分是多少。
图示 1 2 3 4 5 规约前: 规约后(求和):进程0: 1 进程0: 10 进程1: 2 --> 进程1: 10 进程2: 3 进程2: 10 进程3: 4 进程3: 10
例子 1 2 3 4 5 6 7 8 int my_data = rank + 1 ;int result; MPI_Allreduce(&my_data, &result, 1 , MPI_INT, MPI_SUM, MPI_COMM_WORLD);
对比
操作
结果在谁那
Reduce
只有root有
Allreduce
人人都有
记忆 1 2 3 4 Reduce = 算完给老师Allreduce = 算完给所有人All = 所有人都得到结果
常见用途
MPI_Scan 一句话解释 前缀运算,每个进程得到”自己及之前所有进程”的累计结果
类比 报数累加:每人报出从第一个人到自己的总和。
图示 1 2 3 4 5 扫描前: 扫描后(求和): 进程0: 1 进程0: 1 (=1) 进程1: 2 --> 进程1: 3 (=1+2) 进程2: 3 进程2: 6 (=1+2+3) 进程3: 4 进程3: 10 (=1+2+3+4)
例子 1 2 3 4 5 6 7 8 9 10 int my_data = rank + 1 ;int result; MPI_Scan(&my_data, &result, 1 , MPI_INT, MPI_SUM, MPI_COMM_WORLD);
对比
操作
结果
Reduce
只有root有总和
Allreduce
人人有总和
Scan
每人有前缀和
记忆 1 2 3 Scan = 扫描累加进程i得到: 进程0 + 进程1 + ... + 进程i
常见用途
MPI_Finalize 和 MPI_Finalized
MPI_Finalize 一句话解释 结束MPI,清理资源
用法
注意
调用后不能再用任何MPI函数
每个进程都要调用
程序结尾必须调用
MPI_Finalized 一句话解释 查询MPI是否已结束
用法 1 2 3 4 5 6 7 8 int flag; MPI_Finalized(&flag);if (flag) { } else { }
对比
函数
作用
MPI_Init
开始MPI
MPI_Initialized
查是否开始了
MPI_Finalize
结束MPI
MPI_Finalized
查是否结束了
典型程序结构 1 2 3 4 5 6 7 8 int main () { MPI_Init(NULL , NULL ); MPI_Finalize(); return 0 ; }
记忆 1 2 3 4 5 Init = 开门Finalize = 关门Initialized = 门开了吗?Finalized = 门关了吗?
MPI_Send 和 MPI_Recv
一句话解释 Send发送数据,Recv接收数据
类比 寄信:
图示 1 2 3 4 进程0 进程1 | | |------- 数据 ---------> | Send Recv
用法 MPI_Send(发送) 1 2 3 4 5 6 7 8 MPI_Send( &data, count, MPI_INT, dest, tag, MPI_COMM_WORLD );
MPI_Recv(接收) 1 2 3 4 5 6 7 8 9 MPI_Recv( &data, count, MPI_INT, source, tag, MPI_COMM_WORLD, &status );
完整例子 1 2 3 4 5 6 7 8 9 10 int data;if (rank == 0 ) { data = 100 ; MPI_Send(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD); } else if (rank == 1 ) { MPI_Recv(&data, 1 , MPI_INT, 0 , 0 , MPI_COMM_WORLD, &status); }
重要参数
参数
说明
dest/source
对方进程号
tag
必须匹配
MPI_ANY_SOURCE
接收任意来源
MPI_ANY_TAG
接收任意标签
注意
阻塞 :Send/Recv完成前不返回
匹配 :source、dest、tag要对应
死锁 :两个进程互相等对方会卡住
记忆 1 2 3 4 Send = 寄信(目的地 + 标签) Recv = 收信(来源 + 标签) 标签要匹配,否则收不到
MPI 中有阻塞和非阻塞两种
阻塞(Blocking)- 等完才继续 1 2 3 4 MPI_Send() MPI_Recv() MPI_Bcast() MPI_Reduce()
必须等操作完成,程序才往下走。
非阻塞(Non-blocking)- 不等,继续干别的 1 2 3 4 MPI_Isend() MPI_Irecv() MPI_Ibcast() MPI_Ireduce()
发起请求就返回,可以边算边通信。
简单对比
类型
函数名
特点
阻塞
MPI_Send
等待完成
非阻塞
MPI_Isend
立即返回,需要 MPI_Wait
非阻塞需要配合 Wait 1 2 3 4 5 6 MPI_Request req; MPI_Isend(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD, &req); MPI_Wait(&req, &status);
你代码里用的都是阻塞的 1 2 MPI_Bcast() MPI_Reduce()
大多数情况用阻塞就够了,简单安全。
MPI 考试复习要点
1. 基础函数(必考) 1 2 3 4 MPI_Init(&argc, &argv); MPI_Finalize(); MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size);
2. 点对点通信 1 2 MPI_Send(&data, count, type, dest, tag, comm); MPI_Recv(&data, count, type, source, tag, comm, &status);
注意:Send 和 Recv 必须配对,否则死锁!
3. 集合通信(重要)
函数
作用
MPI_Bcast
一对多广播
MPI_Reduce
多对一规约
MPI_Allreduce
规约后所有进程都有结果
MPI_Scatter
一对多分发(每人不同数据)
MPI_Gather
多对一收集
MPI_Barrier
同步,等所有进程到达
4. 常用数据类型
MPI类型
C类型
MPI_INT
int
MPI_DOUBLE
double
MPI_FLOAT
float
MPI_CHAR
char
5. 常用规约操作
操作
含义
MPI_SUM
求和
MPI_MAX
最大值
MPI_MIN
最小值
MPI_PROD
乘积
6. 死锁问题(常考) 1 2 3 4 5 6 7 8 if (rank == 0 ) { MPI_Recv(...); MPI_Send(...); } else { MPI_Recv(...); MPI_Send(...); }
1 2 3 4 5 6 7 8 if (rank == 0 ) { MPI_Send(...); MPI_Recv(...); } else { MPI_Recv(...); MPI_Send(...); }
7. 运行命令 1 2 mpicc -o prog prog.c mpirun -np 4 ./prog
8. 并行计算加速比 1 2 加速比 = 串行时间 / 并行时间 理想情况:用 n 个进程,加速 n 倍
9. Reduce vs Allreduce 1 2 MPI_Reduce() MPI_Allreduce()
10. 常见考题类型
填空 :补全 MPI 函数参数
改错 :找死锁、类型不匹配
编程 :并行求和、求π、矩阵运算
分析 :计算加速比、通信开销
还有什么具体想深入的吗?
常见 MPI 考题类型详解
1. 填空题 题目形式 :给不完整代码,补全参数
1 2 MPI_Bcast(&n, ___, ___, ___, MPI_COMM_WORLD);
答案 :
1 2 3 MPI_Bcast(&n, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
2. 改错题 题目形式 :给有 bug 的代码,找出并修正
1 2 3 int n = 100 ; MPI_Bcast(&n, 1 , MPI_DOUBLE, 0 , MPI_COMM_WORLD);
答案 :
1 2 MPI_Bcast(&n, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
常见错误类型 :
类型不匹配
死锁
忘记 Init/Finalize
Send/Recv 不配对
3. 编程题 题目形式 :写完整并行程序
例1:并行求数组和
答案思路 :
1 2 3 4 5 6 7 int local_sum = 0 ;for (i = rank * 25 ; i < (rank + 1 ) * 25 ; i++) { local_sum += a[i]; } MPI_Reduce(&local_sum, &total, 1 , MPI_INT, MPI_SUM, 0 , MPI_COMM_WORLD);
例2:并行求 π (你代码里那个)
4. 分析题 题目形式 :计算或分析性能
例1:计算加速比
1 2 3 串行程序运行时间:8秒 用4个进程并行:3秒 求加速比
答案 :
例2:分析通信开销
总结表格
题型
考什么
难度
填空
记住函数参数
⭐
改错
理解常见错误
⭐⭐
编程
会写并行程序
⭐⭐⭐
分析
理解并行原理
⭐⭐
需要我出几道练习题给你做吗?
MPI 练习题
一、填空题(每空 2 分) 第 1 题 1 2 3 double data[100 ]; MPI_Bcast(_____, _____, _____, _____, MPI_COMM_WORLD);
第 2 题 1 2 3 int local_sum, total; MPI_Reduce(&local_sum, &total, _____, _____, _____, _____, MPI_COMM_WORLD);
第 3 题 1 2 3 4 int rank, size; MPI_Comm_rank(_____, _____); MPI_Comm_size(_____, _____);
二、改错题(每题 5 分) 第 4 题 找出下面代码的错误并改正:
1 2 double x = 3.14 ; MPI_Bcast(&x, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
第 5 题 下面代码会死锁,请改正:
1 2 3 4 5 6 7 if (rank == 0 ) { MPI_Recv(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD, &status); MPI_Send(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD); } else if (rank == 1 ) { MPI_Recv(&data, 1 , MPI_INT, 0 , 0 , MPI_COMM_WORLD, &status); MPI_Send(&data, 1 , MPI_INT, 0 , 0 , MPI_COMM_WORLD); }
第 6 题 找出错误:
1 2 3 4 5 6 7 int main () { int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); printf ("I am process %d\n" , rank); MPI_Finalize(); return 0 ; }
三、编程题 第 7 题(10 分) 用 4 个进程并行计算数组求和:
1 2 3 数组 a [100] = {1 , 2 , 3 , ..., 100 } 每个进程计算 25 个元素的和 最后把结果汇总到进程 0
写出核心代码。
第 8 题(10 分) 用 MPI 实现并行求最大值:
每个进程有一个局部变量 local_max
找出所有进程中的最大值
结果存到进程 0
四、分析题 第 9 题(5 分) 1 2 3 4 5 6 串行程序运行 12 秒 用 4 个进程并行运行 4 秒 用 8 个进程并行运行 2.5 秒 (1) 计算 4 进程和 8 进程的加速比 (2) 哪个效率更高?为什么?
第 10 题(5 分) 解释 MPI_Reduce 和 MPI_Allreduce 的区别,各在什么场景下使用?
参考答案
点击展开答案
填空题答案 第 1 题
1 MPI_Bcast(data, 100 , MPI_DOUBLE, 0 , MPI_COMM_WORLD);
第 2 题
1 MPI_Reduce(&local_sum, &total, 1 , MPI_INT, MPI_SUM, 0 , MPI_COMM_WORLD);
第 3 题
1 2 MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size);
改错题答案 第 4 题
1 2 MPI_Bcast(&x, 1 , MPI_DOUBLE, 0 , MPI_COMM_WORLD);
第 5 题
1 2 3 4 5 6 7 8 if (rank == 0 ) { MPI_Send(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD); MPI_Recv(&data, 1 , MPI_INT, 1 , 0 , MPI_COMM_WORLD, &status); } else if (rank == 1 ) { MPI_Recv(&data, 1 , MPI_INT, 0 , 0 , MPI_COMM_WORLD, &status); MPI_Send(&data, 1 , MPI_INT, 0 , 0 , MPI_COMM_WORLD); }
第 6 题
1 2 3 4 5 6 7 8 9 int main (int argc, char *argv[]) { MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); printf ("I am process %d\n" , rank); MPI_Finalize(); return 0 ; }
编程题答案 第 7 题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int a[100 ];int rank, size;int local_sum = 0 , total = 0 ; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size);if (rank == 0 ) { for (int i = 0 ; i < 100 ; i++) a[i] = i + 1 ; } MPI_Bcast(a, 100 , MPI_INT, 0 , MPI_COMM_WORLD);int start = rank * 25 ;int end = start + 25 ;for (int i = start; i < end; i++) { local_sum += a[i]; } MPI_Reduce(&local_sum, &total, 1 , MPI_INT, MPI_SUM, 0 , MPI_COMM_WORLD);if (rank == 0 ) { printf ("Total = %d\n" , total); }
第 8 题
1 2 3 4 5 6 7 8 9 int local_max, global_max; MPI_Reduce(&local_max, &global_max, 1 , MPI_INT, MPI_MAX, 0 , MPI_COMM_WORLD);if (rank == 0 ) { printf ("Max = %d\n" , global_max); }
分析题答案 第 9 题
1 2 3 4 5 6 7 8 9 (1 ) 加速比: 4 进程:12 / 4 = 3 8 进程:12 / 2.5 = 4.8 (2 ) 4 进程效率更高 4 进程效率:3 / 4 = 75 % 8 进程效率:4.8 / 8 = 60 % 原因:进程越多,通信开销越大
第 10 题
1 2 3 4 5 6 7 区别:- MPI_Reduce:结果只在 root 进程 - MPI_ Allreduce:结果所有进程都有 使用场景:- MPI_Reduce:只有一个进程需要结果(如最后输出) - MPI_ Allreduce:所有进程都需要结果继续计算
其他 MPI 考点补充
1. 非阻塞通信 阻塞 vs 非阻塞
1 2 3 4 5 6 7 8 MPI_Send(); MPI_Recv(); MPI_Isend(); MPI_Irecv(); MPI_Wait();
用途 :提高效率,边通信边计算
2. 通信子(Communicator)
可能考 :
3. 进程拓扑(可能考概念)
拓扑
说明
线性
进程排成一排
环形
首尾相连
网格
二维排列
4. MPI_Sendrecv 一次同时发送和接收 :
1 2 3 MPI_Sendrecv(&send_data, 1 , MPI_INT, dest, tag, &recv_data, 1 , MPI_INT, source, tag, MPI_COMM_WORLD, &status);
好处 :避免死锁
5. MPI_Wtime 计时函数 :
1 2 3 4 5 double start, end; start = MPI_Wtime(); end = MPI_Wtime();printf ("用时: %f 秒\n" , end - start);
6. 数据划分方式
方式
说明
例子(4进程分100个)
块划分
连续分
0-24, 25-49, 50-74, 75-99
循环划分
轮流分
0,4,8… / 1,5,9…
7. Scatter 和 Gather 1 2 3 4 5 6 7 8 int sendbuf[4 ] = {10 , 20 , 30 , 40 };int recvbuf; MPI_Scatter(sendbuf, 1 , MPI_INT, &recvbuf, 1 , MPI_INT, 0 , MPI_COMM_WORLD); MPI_Gather(&recvbuf, 1 , MPI_INT, sendbuf, 1 , MPI_INT, 0 , MPI_COMM_WORLD);
8. 并行效率公式(常考) 1 2 3 4 5 加速比 S = T串行 / T并行 效率 E = S / P = T串行 / (P × T并行) 其中 P = 进程数
理想情况 :S = P,E = 100%
实际情况 :S < P,因为有通信开销
9. Amdahl 定律(可能考) 1 2 3 4 加速比 ≤ 1 / (f + (1-f)/ P) f = 串行部分比例 P = 进程数
含义 :程序中串行部分越多,加速越有限
例子 :
程序有 10% 必须串行
即使无限多进程,最多加速 10 倍
10. 常见并行模式
模式
说明
主从模式
进程0分配任务,其他执行
对等模式
所有进程地位相同
流水线
数据一级一级传递处理
补充练习题 第 11 题(填空) 1 2 3 4 5 6 double start, end; start = _____(); end = _____();printf ("时间: %f\n" , _____ - _____);
第 12 题(计算) 1 2 程序串行部分占 20 根据 Amdahl 定律,理论最大加速比是多少?
第 13 题(简答) MPI_Send 和 MPI_Isend 有什么区别?
答案
第 11 题
1 2 3 start = MPI_Wtime(); end = MPI_Wtime();printf ("时间: %f\n" , end - start);
第 12 题
1 2 3 4 5 f = 0.2, P = 4 S = 1 / (0.2 + 0.8/4) = 1 / (0.2 + 0.2) = 1 / 0.4 = 2.5
第 13 题
1 2 MPI_Send :阻塞,必须等数据发完才返回MPI_Isend :非阻塞,立刻返回,后面用 MPI_Wait 等待完成
PI monte Carlo 并行实现示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <math.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <mpi.h> int main (int argc, char *argv[]) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); int n_puntos = 10000000 ; double PI25DT = 3.141592653589793238462643 ; double piR; MPI_Bcast(&n_puntos, 1 , MPI_INT, 0 , MPI_COMM_WORLD); if (n_puntos <= 0 ) { MPI_Finalize(); exit (0 ); } else { int dentro = 0 ; double x, y, pi; srand(time(NULL )); for (int i = rank + 1 ; i < n_puntos; i += size) { x = (double )rand() / RAND_MAX * 2 - 1 ; y = (double )rand() / RAND_MAX * 2 - 1 ; if (x * x + y * y <= 1 ) { dentro++; } } pi = 4.0 * dentro / n_puntos; MPI_Reduce(&pi, &piR, 1 , MPI_DOUBLE, MPI_SUM, 0 , MPI_COMM_WORLD); if (rank == 0 ) { printf ("Pi aproximado: %f\n" , piR); printf ("Puntos usados: %d\n" , n_puntos); printf ("Resultado de pi es: %f con un error de %f" , piR, fabs (PI25DT - piR)); } } MPI_Finalize(); return 0 ; }
Pi Riemann 并行实现示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <mpi.h> #include <stdio.h> #include <stdlib.h> #include <math.h> int main (int argc, char **argv) { int size, rank; int n = 20000 ; double PI = 3.141592653589793238462643 ; double pi; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Bcast(&n, 1 , MPI_INT, 0 , MPI_COMM_WORLD); if (n <= 0 ) { MPI_Finalize(); exit (0 ); } else { double h = 1.0 / (double )n; double sum = 0.0 ; for (int i = rank + 1 ; i <= n; i += size) { double x = h * ((double )i - 0.5 ); sum += (4.0 / (1.0 + x * x)); } double mypi = sum * h; MPI_Reduce(&mypi, &pi, 1 , MPI_DOUBLE, MPI_SUM, 0 , MPI_COMM_WORLD); if (rank == 0 ) { printf ("El valor aproximado de pi es: %f con un error de %f" , pi, fabs (pi - PI)); } } MPI_Finalize(); return 0 ; }
Envio y Recepcion de Mensajes en MPI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <mpi.h> #include <stdio.h> int main (int argc, char **argv) { int rank, size; int contador; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0 ) { MPI_Send(&rank, 1 , MPI_INT, rank + 1 , 0 , MPI_COMM_WORLD); } else { MPI_Recv(&contador, 1 , MPI_INT, rank - 1 , 0 , MPI_COMM_WORLD, MPI_STATUS_IGNORE); printf ("Soy el proceso %d y he recibido %d\n" , rank, contador); contador++; if (rank != size - 1 ) { MPI_Send(&contador, 1 , MPI_INT, rank + 1 , 0 , MPI_COMM_WORLD); } } MPI_Finalize(); return 0 ; }