网络拓扑很重要
不同拓扑有不同的直径、对分带宽和成本。现代系统(Dragonfly)使用层次化拓扑。
CS267 · 第 7 课 · Aydin Buluc
分布式内存系统通过高速网络连接独立的节点。MPI(Message Passing Interface)是分布式内存编程的工业标准,通过消息传递实现进程间通信。
TOP500 中系统架构的演进:
早期分布式内存机器:
网络就像街道系统:
| 属性 | 描述 |
|---|---|
| Topology | 连接方式(crossbar、ring、mesh、torus、hypercube、tree、butterfly、dragonfly) |
| Routing | 路由算法(避免死锁) |
| Switching | Circuit switching vs Packet switching |
| Flow control | 拥塞控制(stall、buffer、reroute、halt) |
将对网络切成两半的最小割的带宽:
| 拓扑 | Bisection BW |
|---|---|
| Linear Array | 1 |
| Ring | 2 |
| 2D Mesh | \(\sqrt{n}\) |
| 2D Torus | \(2\sqrt{n}\) |
| Hypercube | \(n/2\) |
| 拓扑 | Diameter | Bisection BW | 特点 |
|---|---|---|---|
| Linear/Ring | \(O(n)\) / \(O(n)\) | 1 / 2 | 简单,但可扩展性差 |
| 2D Mesh | \(2(\sqrt{n}-1)\) | \(\sqrt{n}\) | 适合 2D 数组 |
| 2D Torus | \(\sqrt{n}\) | \(2\sqrt{n}\) | Cray XT (Hopper) 使用 3D Torus |
| Hypercube | \(\log n\) | \(n/2\) | 早期机器(Intel iPSC, NCUBE) |
| Tree | \(\log n\) | 1 | Fat Tree 解决带宽瓶颈 |
| Butterfly | \(\log n\) | \(n\) | Hypercube 的展开版本 |
| Dragonfly | ~5 hops | - | Edison、Cori 使用 |
利用光互连(机柜间)和电互连(机柜内)的成本/性能差异:
发送长度为 n 的消息的时间:
\[T = \alpha + n \cdot \beta\]
教训:一条长消息比许多短消息便宜。需要大的计算/通信比才能高效。
Parallel Random Access Memory:
历史上多种消息传递库:
#include "mpi.h"
#include <stdio.h>
int main(int argc, char *argv[]) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("I am %d of %d\n", rank, size);
MPI_Finalize();
return 0;
}
运行:mpirun -np 4 a.out
大多数 MPI 程序只需 6 个函数:
| Point-to-point | Collective |
|---|---|
| MPI_INIT | MPI_INIT |
| MPI_FINALIZE | MPI_FINALIZE |
| MPI_COMM_SIZE | MPI_COMM_SIZE |
| MPI_COMM_RANK | MPI_COMM_RANK |
| MPI_SEND | MPI_BCAST |
| MPI_RECV | MPI_REDUCE |
// 发送
MPI_Send(start, count, datatype, dest, tag, comm);
// 接收
MPI_Recv(start, count, datatype, source, tag, comm, status);
// 每个进程计算自己的部分
h = 1.0 / n;
sum = 0.0;
for (i = myid + 1; i <= n; i += numprocs) {
x = h * (i - 0.5);
sum += 4.0 * sqrt(1.0 - x*x);
}
mypi = h * sum;
// 归约
MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
// "Unsafe" 代码 — 依赖系统缓冲
Process 0: Send(1) Recv(1)
Process 1: Send(0) Recv(0)
// 大消息时可能死锁!
| 方案 | 描述 |
|---|---|
| MPI_Sendrecv | 同时发送和接收 |
| MPI_Bsend | 用户提供的缓冲 |
| MPI_Isend/Irecv | 非阻塞操作 |
MPI_Request request;
MPI_Status status;
// 非阻塞发送
MPI_Isend(start, count, datatype, dest, tag, comm, &request);
// 非阻塞接收
MPI_Irecv(start, count, datatype, source, tag, comm, &request);
// 等待完成
MPI_Wait(&request, &status);
// 或测试是否完成
MPI_Test(&request, &flag, &status);
| 模式 | 描述 |
|---|---|
MPI_Send | 标准模式(实现决定缓冲) |
MPI_Ssend | 同步模式(等待接收开始) |
MPI_Bsend | 缓冲模式(用户提供缓冲) |
MPI_Rsend | 就绪模式(接收已发布) |
Q1:α-β 模型中,\(\alpha\) 和 \(\beta\) 分别代表什么?
Q2:2D Torus 的直径是多少?
Q3:非阻塞通信的主要优点是什么?
Q4:MPI 中,大多数程序最少需要几个函数?
Q5:Dragonfly 拓扑使用什么路由算法?
不同拓扑有不同的直径、对分带宽和成本。现代系统(Dragonfly)使用层次化拓扑。
延迟 \(\alpha\) 远大于带宽倒数 \(\beta\)。需要大的计算/通信比才能高效。
MPI 提供了丰富的通信原语。大多数程序只需 6 个函数。
MPI_Isend/Irecv 允许重叠通信和计算,是高性能的关键。