分布式chap-5

消息传递编程

进程

  • 启动时指定数量
  • 在整个程序执行过程中保持不变
  • 都执行相同的程序
  • 每个都有唯一的 ID 号
  • 交替执行计算和通信

消息传递模型的优势

  • 使程序员能够管理内存层次结构
  • 许多架构的可移植性
  • 更容易创建确定性程序
  • 简化调试

消息传递接口历史

  • 1980 年代后期:供应商拥有独特的库
  • 1989 年:在橡树岭国家实验室开发的并行虚拟机 (PVM)
  • 1992 年:开始制定 MPI 标准
  • 1994 年:MPI 标准 1.0 版
  • 1997 年:MPI 标准 2.0 版
  • 今天:MPI 是主要的消息传递库标准

聚合与映射

  • 并行算法的特性

    • 固定任务数
    • 任务之间没有通信
    • 每个任务所需的时间是可变的
  • 查阅映射策略决策树

    • 以循环方式将任务映射到处理器

循环(交错)分配

假设 p 个进程
每个进程得到每 pth 件工作
示例:5 道工序和 12 件工作
P0: 0, 5, 10
P1:1、6、11
P2: 2, 7
P3: 3, 8
P4:4、9

编程

头文件

1
2
3
4
5
6
7
8
9
//mpi依赖
#include<mpi.h>
#include<stdio.h>

int main (int argc, char *argv[]) {
int i;
int id; /* Process rank */
int p; /* Number of processes */
void check_circuit (int, int);
  • argc 和 argv:初始化 MPI 需要它们
  • 运行此程序的每个进程的每个变量的一份副本

初始化

1
MPI_Init (&argc, &argv);
  • 每个进程调用的第一个 MPI 函数
  • 不一定是第一个可执行语句
  • 允许系统进行任何必要的设置

Communicator 通信器:为进程提供消息传递环境的不透明对象
MPI_COMM_WORLD
默认的通讯器
包括所有进程
可以创建新的通信器
将在第 8 章和第 9 章中执行此操作

image-20210605011229779

确定进程数

1
MPI_Comm_size (MPI_COMM_WORLD, &p);

第一个参数是 Communicator 通信器
第二个是返回的进程数

进程数序号

1
MPI_Comm_rank (MPI_COMM_WORLD, &id);

第一个是 Communicator
第二个是进程 id

外部变量

1
2
3
4
5
6
7
int total;

int main (int argc, char *argv[]) {
int i;
int id;
int p;

全局变量分配在静态存储区

工作循环分配

1
2
for (i = id; i < 65536; i += p)
check_circuit (id, i);
  • 外部功能检查电路具有并行性
  • 它可以是一个普通、串行的函数

关闭 MPI

1
MPI_Finalize();
  • 在所有其他 MPI 库调用之后调用
  • 允许系统释放 MPI 资源

完整代码

编译 MPI 程序

1
mpicc -O -o foo foo.c
  • mpicc:用于编译和链接 C+MPI 程序的脚本
  • -O 优化
  • -o 可执行文件存放位置

运行

1
mpirun -np <p> <exec> <arg1> …
  • -np

    进程数

  • 可执行文件
  • 命令参数

指定主机进程

文件**.mpi-machines** 在home目录列出主机进程的使用顺序

例如:.mpi-machines 文件目录:

band01.cs.ppu.edu
band02.cs.ppu.edu
band03.cs.ppu.edu
band04.cs.ppu.edu

运行文件其对应的运行结果

一个 CPU
1
2
3
4
5
6
7
8
9
10
% mpirun -np 1 sat0) 1010111110011001
0) 0110111110011001
0) 1110111110011001
0) 1010111111011001
0) 0110111111011001
0) 1110111111011001
0) 1010111110111001
0) 0110111110111001
0) 1110111110111001
Process 0 is done
两个 CPU
1
2
3
4
5
6
7
8
9
10
11
% mpirun -np 2 sat0) 0110111110011001
0) 0110111111011001
0) 0110111110111001
1) 1010111110011001
1) 1110111110011001
1) 1010111111011001
1) 1110111111011001
1) 1010111110111001
1) 1110111110111001
Process 0 is done
Process 1 is done
三个 CPU
1
2
3
4
5
6
7
8
9
10
11
12
% mpirun -np 3 sat0) 0110111110011001
0) 1110111111011001
2) 1010111110011001
1) 1110111110011001
1) 1010111111011001
1) 0110111110111001
0) 1010111110111001
2) 0110111111011001
2) 1110111110111001
Process 1 is done
Process 2 is done
Process 0 is done

输出分析

  • 输出顺序仅部分反映并行计算机内部输出事件的顺序
  • 如果进程 A 打印两条消息,则第一条消息将在第二条之前出现
  • 如果进程 A 在进程 B 之前调用 printf,则不能保证进程 A 的消息会出现在进程 B 的消息之前

改进程序

  • 希望找到解决方案的总数
  • 将总和规约适用于这个程序
  • 规约是一个集合的通信

修改

  • 修改函数 check_circuit
    • 如果电路满足输入组合,则返回 1
    • 否则返回 0
  • 每个进程保持它找到的可满足电路的本地计数
  • 在 for 循环后执行归约

新的声明和代码

1
2
3
4
5
6
7
int count;  /* Local sum */
int global_count; /* Global sum */
int check_circuit (int, int);

count = 0;
for (i = id; i < 65536; i += p)
count += check_circuit (id, i);
MPI_Reduce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int MPI_Reduce (void *operand,
/* addr of 1st reduction element */
void *result,
/* addr of 1st reduction result */
int count,
/* reductions to perform */
MPI_Datatype type,
/* type of elements */
MPI_Op operator,
/* reduction operator */
int root,
/* process getting result(s) */
MPI_Comm comm
/* communicator */
)
MPI_DATATYPE OPTIONS
1
2
3
4
5
6
7
8
9
10
11
MPI_CHAR
MPI_DOUBLE
MPI_FLOAT
MPI_INT
MPI_LONG
MPI_LONG_DOUBLE
MPI_SHORT
MPI_UNSIGNED_CHAR
MPI_UNSIGNED
MPI_UNSIGNED_LONG
MPI_UNSIGNED_SHORT
MPI_OP OPTIONS
1
2
3
4
5
6
7
8
9
10
11
12
MPI_BAND
MPI_BOR
MPI_BXOR
MPI_LAND
MPI_LOR
MPI_LXOR
MPI_MAX
MPI_MAXLOC
MPI_MIN
MPI_MINLOC
MPI_PROD
MPI_SUM

调用 MPI_Reduce

1
2
3
4
5
6
7
8
MPI_Reduce (&count,
&global_count,
1,
MPI_INT,
MPI_SUM,
0,//only process 0 will get the result
MPI_COMM_WORLD);
if (!id) printf ("There are %d different solutions\n",global_count);

第二个程序执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
% mpirun -np 3 seq20) 0110111110011001
0) 1110111111011001
1) 1110111110011001
1) 1010111111011001
2) 1010111110011001
2) 0110111111011001
2) 1110111110111001
1) 0110111110111001
0) 1010111110111001
Process 1 is done
Process 2 is done
Process 0 is done
There are 9 different solutions

MPI 基准测试程序

  • MPI_Barrier – barrier synchronization 障碍同步
  • MPI_Wtick – timer resolution 时钟分辨率
  • MPI_Wtime – current time 当前时间

wall-clock time VS CPU time(挂钟时间和 CPU 时间)

  • CPU time (or process time)

    • 是 CPU 用于处理指令的时间量。
    • 用户时间+系统时间
  • Wall-clock time (or wall time )

    • 从开始到完成任务所经过的时间。

    • CPU 时间、I/O 时间和通信通道延迟

基准测试代码

1
2
3
double elapsed_time;…
MPI_Init (&argc, &argv); MPI_Barrier (MPI_COMM_WORLD); elapsed_time = - MPI_Wtime(); …
MPI_Reduce (…); elapsed_time += MPI_Wtime();

结果

Processors Time (sec)
1 15.93
2 8.38
3 5.86
4 4.60
5 3.77
image-20210605153336261
作者

Erial

发布于

2021-06-03

更新于

2023-02-16

许可协议

评论