一. MPI通信:组通信
1. 分类
- 一对多
- 多对一
- 多对多
- 同步
2. 广播
- 该节点不会把数据传送给自己
- 0号进程刚开始就为100
3. Scatter
- 会给各个进程发一个不同的消息 包括自己
- 发送数据按照缓冲区排列顺序
4. Gather
- 接收各个进程(包括自己)的消息
// 每个进程都需要调用这个接口,需要和0号进程进行通信
// 用0号进程收集
MPI_Gather(&myRows[1][0], 64, MPI_FLOAT, c, 64, MPI_FLOAT, 0, MPI_COMM_WORLD);
5. Allgather(多对多)
- 每个进程都会获得这么长数据的副本
6. Reduce(归约)
- 类似gather,会对数据进行规约操作
- 提供操作
MPI_Reduce
- 先进行同步,在进行规约
- 只有根进程有存储结果,其他进程为0
7. MPI_Allreduce
- 所有进程都会获得规约结果
8. MPI_Reduce_scatter
- 每个进程只对不同的数据位置进行规约
- 把globalData散发到localData中
9. MPI_Scan
- 对自身进程之前的进程进行规约操作
- 规约比较
10. MPI_Alltoall
- 矩阵转置
- 一般用于系统的通信性能稳定性测试
11. MPI_Barrier
- 先到达先等待,等最后一个
总结
二、圆周率
1. 积分串行并行
省略
2. 蒙特卡洛
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctime>
#include <cstdlib>
#include <random>
#include <iostream>
#include <mpi.h>
int main(int argc, char ** argv){
long long N, nn, n1, nt;
double x, y;
int myrank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (myrank == 0){
N = atoi(argv[1]);
}
MPI_Bcast(&N, 1, MPI_LONG_LONG, 0, MPI_COMM_WORLD);
nn = N / size;
n1 = 0;
uniform_real_distribution<double> u(-1, 1);
default_random_engine e(time(NULL) + myrank);
for (int i = 0; i < nn; i ++){
double x = u(e);
double y = u(e);
if (x * x + y * y <= 1.0) n1 ++;
}
MPI_Reduce(&n1, &nt, 1, MPI_LONG_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
if (myrank == 0)
cout << "result = " << 4 * nt / N << endl;
MPI_Finalize();
return 0;
}
3. 并行性能分析
4. 出现的问题
- 负载不均衡
- 静态均衡
- 手动分配积分区间
- 动态均衡
- 实际运行过程,每个迭代步都需要对数据迁移
- 迭代一般都需要进行数据交换
- 数据分块和区域分解的区别
三、MPI通信模式
-
通信模式
-
缓存通信模式
-
同步通信模式
- 发送和接收有一个握手过程
- 就绪通信模式
- 通信特殊情况
- 短时间内有大量的短消息的发送
- 导致缓冲区爆掉,消息丢失。用户需要使用
MPI_Bsend
控制缓冲区使用 - 短消息打包成大消息,降低发送次数
- 导致缓冲区爆掉,消息丢失。用户需要使用
- 发送数据量特别大(10GB)
- MPI提供的缓存区大小不够,导致数据丢失
MPI_Bsend
直接控制缓冲区使用- 可以分段发送,一次性发送100MB,然后发送100次
- 如果是大程序,很难判断是哪里出现了问题
四、 MPI自定义数据类型
自定义数据类型:发送不连续数据
派生数据类型
-
MPI_Type_struct
- 参数
- 参数
-
C语言对齐
五、MPI打包与解压
新建一个struct需要很多其他的代码
1. 打包
2. 解压
广播自带一个同步,会等其他进程进行到这再广播,自带barrier
六、MPI虚拟进程拓扑
- 笛卡尔拓扑
- 笛卡尔拓扑偏移量