CS267 · 第 25 课

图划分与负载均衡

如何将计算任务均衡地分配到多个处理器上,同时最小化通信开销?这是并行计算中最核心、也最困难的问题之一。本课从图划分的数学理论出发,深入讨论 Kernighan/Lin、谱划分、多重网格加速等经典算法,并介绍动态负载均衡中的 work stealing 技术。

授课:Jim Demmel (Part A) / Kathy Yelick (Part B)

日期:2025-04-22

难度:困难

预计用时:80 分钟

视频:Lecture 25 Video

Part A:图划分(Graph Partitioning)

1. 图划分问题定义

给定一个图 G = (N, E, WN, WE),其中:

图划分的目标是找到 N 的一个划分 N = N1 ∪ N2 ∪ ... ∪ NP,使得:

  1. 负载均衡:每个节点子集 Nj 的节点权重之和"大致相等"
  2. 最小化通信:连接不同子集的所有边的权重之和最小化

当 P = 2 时,称为图二分(Graph Bisection),这是构建更多划分的递归基础。

理解图划分的直觉

将图划分类比为"切蛋糕":节点是蛋糕上的"葡萄干"(带重量),边是连接葡萄干的"丝带"。我们要用刀把蛋糕切成 P 块,使得:(1)每块上的葡萄干总重量相等,(2)切断的丝带总长度最短。这正是负载均衡与通信最小化的权衡。

2. 计算复杂度

将 n 个节点恰好切成两半的可能方案数为:

C(n, n/2) = n! / ((n/2)!)² ≈ (2/(πn))^(1/2) × 2^n

这是一个指数级的搜索空间。更重要的是,图划分问题是 NP-complete 的——意味着除非 P=NP,否则不存在多项式时间精确算法。我们必须依赖启发式算法。

NP-complete 的含义

NP-complete 意味着图划分与已知的困难问题(如旅行商问题、SAT 问题)一样难。目前已知的精确算法在最坏情况下需要指数时间。因此,所有实际使用的划分工具都采用启发式方法,追求"足够好"的解而非最优解。

3. 应用场景

应用 节点 N 边 E 权重含义
SpMV (y = Ax) 矩阵行/列 非零元素的关联 WN=行非零元数, WE=1
PDE 求解 网格单元 相邻关系 WN=计算成本, WE=通信量
VLSI 布局 芯片单元 连线 WE=连线长度
数据挖掘聚类 数据点 相似度 WE=相似度分数
DNA 物理图谱 基因片段 重叠关系 WE=重叠程度
电话网络设计 交换机 通信线路 WE=线路容量

SpMV 的通信模式示例

// 每个处理器 P 本地计算
y_local = y_local + A_local * x_local

// 获取远程处理器所需的 x 的部分
for all procs P needing part of x_local
    send(needed part of x_local, P)

// 接收远程 x,完成计算
for all procs P owning needed x_remote
    receive(x_remote, P)
    y_local = y_local + A_remote(P) * x_remote

4. Edge Separator vs. Vertex Separator

图划分有两种互补的视角:

概念 定义 代价度量
Edge Separator (Es) 边集 E 的子集,移除后图分裂为两个近似等大的连通分量 被切割边的权重和
Vertex Separator (Ns) 节点集 N 的子集,移除后(及关联边)图分裂为两个近似等大的连通分量 分隔节点的权重和

相互转换:

5. 有坐标的图划分

当图具有空间坐标时(如有限元模型中的节点位于 (x, y, z) 空间),可以利用几何信息高效划分。

Inertial Partitioning(惯性划分)

选择一个通过图"中心"的划分平面,使得平面两侧的节点数大致相等。对于二维图,可以选择一条直线(如沿主轴),将节点投影后在中位数处切分。

平面图的 Lipton-Tarjan 定理

定理 (Tarjan, Lipton, 1979): 若 G 是平面图(可在平面上绘制且边不相交),则存在顶点分隔集 Ns,使得:

例如:m × m 网格有 m² 个节点,仅需 m = |N|1/2 个分隔节点。这比任意图的 O(|N|) 分隔好得多!

几何方法总结

优点 缺点
高效(通常 O(|N| log |N|)) 依赖节点的空间最近邻连接假设
不需要知道实际边的位置 对"长程连接"图效果差(如 WWW 图)
可作为后续算法的初始猜测 可能产生次优划分,尤其当图结构复杂时

6. Kernighan/Lin 算法

Kernighan/Lin (1970) 是最早的图划分启发式算法之一,通过迭代改进初始划分来降低切割代价。

核心思想

给定划分 N = A ∪ B,定义代价 T = cost(A,B) = Σ W(e)(跨越 A 和 B 的边)。选择子集 X ⊆ A 和 Y ⊆ B(|X| = |Y|),考虑交换它们:

newA = (A - X) ∪ Y
newB = (B - Y) ∪ X
newT = cost(newA, newB) < T

关键定义

交换节点 a 和 b 的收益:

gain(a,b) = D(a) + D(b) - 2 × W(a,b)
newT = T - gain(a,b)

算法流程

计算初始代价 T = cost(A,B)                         // O(|N|²)

Repeat
    计算所有节点的 D(n)                              // O(|N|²)
    标记所有节点为未移动

    While 还有未移动节点                            // |N|/2 轮
        在未移动节点中找到 (a,b) 最大化 gain(a,b)   // O(|N|²)
        标记 a 和 b(但不实际交换)
        更新所有未移动节点的 D(n)                    // O(|N|)
    EndWhile

    // 找到最佳前缀 (a1,b1)...(am,bm) 使累计 Gain 最大
    选择 m 最大化 Gain = Σ gain(k)                  // O(|N|)

    If Gain > 0
        执行交换,更新划分和 T
Until Gain <= 0

时间复杂度:O(|N|³)(瓶颈在未标记节点的配对搜索)。

Fiduccia-Mattheyses 改进 (1982)

FM 算法将复杂度降至 O(|E|),关键改进:

K/L 算法的注意事项

7. 谱划分(Spectral Bisection)

谱划分由 Fiedler (1970s) 提出,Pothen/Simon/Liou (1990) 推广,基于图的 Laplacian 矩阵的特征向量。

振动弦的类比

将图想象为一根振动的弦:节点是质量块(质量),边是弹簧。弦有不同的振动模式(谐波),第二阶模式的正负号自然地将弦分为两部分!

Laplacian 矩阵 L(G)

对于图 G(N,E),定义 |N| × |N| 对称矩阵:

L(G)(i,i) = node i 的度数(关联边数)
L(G)(i,j) = -1  (若 i ≠ j 且存在边 (i,j))
L(G)(i,j) = 0   (其他情况)

Laplacian 矩阵的性质

定理:

谱划分算法

1. 计算 L(G) 的第二特征向量 v₂(对应 λ₂)
2. 对于每个节点 n:
     若 v₂(n) < 0 → 放入分区 N⁻
     否则         → 放入分区 N⁺

为什么谱划分有效?

Fiedler 定理 (1975): 若 G 连通,按 v₂ 符号划分的 N⁻ 和 N⁺ 各自连通。图中"薄弱连接"处的节点在 v₂ 中接近 0,自然成为分隔点。

有趣的循环依赖: 为了优化 SpMV,需要图划分;为了图划分,需要特征向量;为了特征向量,需要做稀疏矩阵向量乘法(SpMV)!循环并非无意义——先用慢的 SpMV 学出结构,再加速后续计算。

计算:Lanczos 算法

对于大型稀疏矩阵,使用 Lanczos 迭代法计算 v₂,其每步仅需一次稀疏矩阵-向量乘法(SpMV)。

8. 多重网格加速(Multilevel)

这是科学计算中反复出现的核心思想:当图太大无法高效划分时,先粗化、再划分、再细化。

Multilevel_Partition 算法

(N⁺, N⁻) = Multilevel_Partition(N, E)
    if |N| 很小
        (1) 直接划分 G = (N,E),返回
    else
        (2) 粗化 G 得到近似图 Gc = (Nc, Ec)
        (3) (Nc⁺, Nc⁻) = Multilevel_Partition(Nc, Ec)  // 递归
        (4) 将 Gc 的划分"扩展"为 G 的初始划分
        (5) 用 K/L 或类似方法改进划分
        返回
    endif

这形成一个 V-cycle:递归向下粗化 → 在最粗层划分 → 逐层向上扩展并改进。

粗化策略:极大匹配(Maximal Matching)

匹配:边集 Em ⊆ E,其中任意两边不共享端点。

极大匹配:无法再添加边而不破坏匹配性质。

// 贪心构造极大匹配
Em = ∅
标记所有节点为未匹配

for i = 1 to |N|                     // 任意顺序访问节点
    if i 未匹配
        if 存在边 (i,j) 且 j 未匹配
            将 (i,j) 加入 Em
            标记 i 和 j 为已匹配
    endif
endfor

粗化过程:将匹配的每对节点坍缩为一个超节点,权重相加;未匹配节点直接保留。

扩展策略:Rayleigh 商迭代

将粗图 Gc 的划分扩展到细图 G:

对于每个节点 j in N:
    if j 也是 Nc 中的节点
        v(j) = vc(j)                    // 直接使用粗特征向量
    else
        v(j) = 邻居节点 vc(k) 的平均值  // 插值
endif

然后使用 Rayleigh 商迭代改进特征向量(三次收敛速度)。

多重网格的核心洞察

在粗层上,图的规模小,划分快;粗划分的质量足以作为细层的良好初始猜测。这类似于多重网格求解 PDE:在粗网格上消除低频误差,在细网格上消除高频误差。

9. 软件实现

软件 算法 特点
METIS / ParMETIS Multilevel K/L 最流行的图划分库,github.com/KarypisLab/METIS
SCOTCH / PT-SCOTCH Multilevel + 谱方法 INRIA 开发,支持分布式并行
Chaco Multilevel Spectral Sandia 国家实验室,获 SC'14 Test of Time Award
Zoltan 多种方法集合 Sandia,支持超图和几何划分
KaHIP 多种高级方法 Karlsruhe,当前质量最佳之一
meshpart (Matlab) 几何 + 谱方法 Gilbert/Tang/Li 实现,教学友好

10. 超图(Hypergraph)模型

传统图模型中,一条边只连接两个节点。但 SpMV 中,一个矩阵列可能涉及多行的通信——这需要超图建模。

列网模型(Column-net Model)

关键优势:若同一列的非零元被分到不同处理器,只需通信一次该列的 x 值!传统图模型会重复计数。

超图示例对比

// SpMV 示例:P1 需要 x(7) 来计算 y(4) 和 y(5)
// 图模型:两条边 (4,7) 和 (5,7) → 通信 x(7) 两次
// 超图模型:一个超边同时包含 4,5,7 → 通信 x(7) 一次!

图 vs. 超图的权衡

维度 图划分 超图划分
非对称矩阵 A+AT 丢失信息 精确建模通信
通信估计 cut size ≠ 通信量 connectivity-1 度量更准确
2D 网格 SpMV 通信量 64 通信量 58(更优)
计算成本 快(METIS) 慢 5-20×(PaToH)

超图划分的代价

PaToH 等超图划分器比 METIS 慢 5-20 倍,但对非对称矩阵和精确通信建模的场景,这个代价是值得的。Zoltan 和 HMETIS 也支持超图划分。

11. 图划分 Myths

Bruce Hendrickson 提出的六个图划分 myths:

# Myth 现实
1 Edge cut = 通信代价 总通信量 ≠ 最大通信量;最大值可能更关键
2 简单图足够 超图/有向图才能精确建模
3 Edge cut 是正确度量 不同架构需要不同度量(如最大 vs 总和)
4 现有工具解决了问题 动态、流式、超图场景仍需新方法
5 关键是找到正确划分 有时划分速度比质量更重要
6 图划分是已解决的问题 非平凡——分布式图、动态图仍是挑战

Label Propagation(标签传播)

一种更轻量、可扩展的替代方案:

1. 随机初始化每个节点的标签(共 n 个标签)
2. 迭代更新:每个节点取邻居中最多的标签(平局随机打破)
3. 可选:加入平衡约束(限制每部分节点/边数)
4. 迭代在约束和目标之间交替优化

PULP 等软件实现了带约束的标签传播,适用于 PowerGraph、GPS 等图处理系统的流式划分。

Part B:Work Stealing 负载均衡

12. 负载不均衡的来源

负载不均衡来自两个层面:

应用层

系统层

性能噪声(performance variability)来自硬件或操作系统:

Heisenbugs in Performance

使用 CrayPat、HPC Toolkit、TAU 等工具分析性能时要注意:测量本身可能改变程序行为!负载不均衡不一定是应用问题,系统噪声同样严重。

任务成本的难度频谱

难度 特征 示例
简单 等成本、固定数量 规则网格、稠密矩阵、直接 N-body
中等 不同但可估计的成本 自适应网格、稀疏矩阵、树 N-body
困难 成本/数量执行中才知 搜索(UTS)、不可预测机器

13. 静态 vs. 动态负载均衡

方法 适用场景 优点 缺点
静态 问题规模已知(n, nnz...) 简单、无运行时开销 估算/优化成本高(图划分)
动态 工作成本未知 自适应性强 需要运行时调度开销

关键权衡:负载均衡与数据局部性经常矛盾——追求完美均衡可能破坏缓存局部性。

14. 独立任务调度

当任务之间无依赖时(如简单的 for 循环),动态调度策略包括:

Self Scheduling(自调度)

所有处理器从共享队列逐个取任务。简单但队列竞争严重。

Chunked Scheduling(分块调度)

每次取 K 个任务作为一块,减少队列访问次数:

// Chunk 大小 K 的权衡
// K 太大 → 负载不均衡(最后几块可能空闲)
// K 太小 → 队列竞争开销大

Guided Self Scheduling(GSS,引导自调度)

动态调整 chunk 大小:第 i 次从任务池取 Ki = ⌈Ri/p⌉ 个任务,其中 Ri 是剩余任务数。

// GSS 示例:100 个任务,4 个处理器
// 第 1 次:取 ⌈100/4⌉ = 25 个
// 第 2 次:取 ⌈75/4⌉ = 19 个
// 第 3 次:取 ⌈56/4⌉ = 14 个
// ...逐渐减小,保证后期细粒度均衡

GSS 的直觉

GSS 前期取大块减少开销,后期取小块保证均衡。就像分蛋糕:开始时每人拿大块(快速推进),到最后几块时仔细分配(避免有人没分到)。

15. Task Stealing(任务窃取)

更高效的方案:每个处理器维护自己的本地队列,空闲时去"偷"别人的任务。

本地 vs. 窃取的双策略

>
操作 策略 原因
取自己的任务 LIFO(栈,从顶部取) 最大化缓存局部性(最近创建的任务数据可能在缓存中)
窃取他人任务 FIFO(队列,从底部取) 窃取"较老"的任务——通常是更大的子树,提供足够工作

窃取目标选择

为什么随机窃取最优?

随机选择窃取目标避免了"羊群效应"(多个处理器同时窃取同一目标)。理论证明:随机 work stealing 的期望调度开销仅为 O(p × T∞),其中 p 是处理器数,T∞ 是关键路径长度。

16. Balls-in-bins 理论

经典概率结果为随机化调度提供理论保证:

定理:将 n 个球随机投入 p 个桶,最大桶的负载(以极高概率)为:

若 n > p log p:   Θ(n/p) + Θ(√(n log(p)/p))
若 n = p:          Θ(log n / log log n)

当 n >> p 时,最大负载接近最优 n/p!这意味着随机化在任务远多于处理器时效果极好。

加倍技巧(Power of Two Choices)

每次随机选 d 个桶,放入最空的那个:最大负载降至 O(log log n / log d)。这是随机化带来的指数级改进!

17. 有依赖的任务

实际任务通常构成有向无环图(DAG),需要更复杂的调度策略。

任务依赖频谱

难度 结构 示例
简单 就绪任务集合 矩阵乘法、域分解
中等 链 / 树 / 图 时间循环、分治算法、直接求解器
困难 运行时才知结构 游戏树搜索、离散事件模拟

任务树:搜索示例

搜索问题(如棋类、N-queens、VLSI 布局、Gröbner 基)的特点是:

并行搜索的 Work Stealing

理论结果 (Blumofe & Leiserson, 1994):对于固定任务树、变成本任务,Cilk 的 work stealing 策略:

Karp & Zhang (1996):对于动态展开的单位成本任务树,随机推送(pushing children to random processors)也是期望最优的。

Cilk 的 Work Stealing 实现

Cilk 语言使用 "work-first" 原则:优先保持本地任务执行的快速性,仅在空闲时窃取。其调度器被称为 "work-stealing scheduler",是 Intel TBB、OpenMP 等后续系统的基础。

18. Charm++ 与过度分解

Charm++ 采用过度分解(over-decomposition)策略:

Charm++ 层次化负载均衡

1. 常规时间步:收集性能数据(instrumented timesteps)
2. 详细负载均衡:激进调整 chares 分布
3. 细化负载均衡:微调优化
4. 拓扑感知:考虑机器拓扑(节点内/节点间)

成功应用:

19. DAG 调度器

对于稠密线性代数等具有规则 DAG 结构的计算,专用调度器可以高效管理任务依赖:

调度器 类型 特点
DAGuE 库 支持稠密线性代数,符号化 DAG 解释
StarPU 库 支持 GPU,分布式数据管理
PLASMA / QUARK 编译器/库 动态调度 DAG 前沿
OpenMP 4.0 语言扩展 task depend 子句,GCC 4.9+
Cilk / TBB 库 fork-join 图专用

DAG 规模挑战

对于 n × n 矩阵、b × b 分块的稠密线性代数:

任务数 = O((n/b)³) = 1,000,000  (当 n=10000, b=100)

创建和调度整个 DAG 不可行!解决方案:

Cholesky 分解的 DAG 示例

POTRF + TRTRI + LAUUM (对称正定矩阵求逆):
- Span (关键路径) = 7(n/b) - 3 = 25  (当 n/b = 4)
- Cholesky 单独: 3(n/b) - 2
- Pipelined: 3(n/b) + 6 = 18

测验

Q1:图划分问题的计算复杂度是什么?

Q2:在谱划分中,使用 Laplacian 矩阵的哪个特征向量来划分图?

Q3:在 Task Stealing 中,为什么本地任务用 LIFO、窃取任务用 FIFO?

Q4:超图划分相比图划分的优势是什么?

Q5:关于 Balls-in-bins 理论,当 n = p 时,随机分配的最大负载是多少?

要点总结

图划分 = 负载均衡 + 通信最小化

NP-complete 问题,需要启发式算法。METIS、SCOTCH、KaHIP 是主流工具。多重网格加速是核心思想:粗化 → 划分 → 扩展 → 改进。

谱划分 vs. K/L 算法

谱划分基于 Laplacian 的第二特征向量,理论优雅但计算代价高。K/L 和 FM 算法更实用,常作为改进模块使用。超图模型对 SpMV 通信建模更准确。

Work Stealing 是最优动态调度

本地 LIFO + 窃取 FIFO + 随机选择目标:理论证明期望意义最优。Cilk、TBB、Charm++ 均采用此策略。过度分解让 Charm++ 支持层次化、拓扑感知的负载均衡。

负载均衡 vs. 局部性的永恒权衡

追求完美均衡可能破坏缓存局部性。理解应用特征(任务成本、依赖结构、通信模式)是选择静态/动态/混合策略的关键。

延伸阅读