《并行计算基础》
1. 并行计算的基本概念
并行计算的目标是通过硬件、系统和应用软件的协同工作,实现计算加速或解决大规模问题。其核心组成部分包括:
- 硬件:多个处理器、多个存储器和互联网络。
- 系统软件:并行操作系统和用于表达并发的编程构造。
- 应用软件:并行算法,用于高效利用硬件资源。
2. 并行算法与并行化
文章区分了“并行算法”和“并行化”:
- 并行算法:可能与串行算法完全不同,是专门为并行环境设计的。
- 并行化:对现有串行算法进行改造,使其能够在并行环境中运行。
文章主要关注并行化,即如何将串行算法改造成并行算法。
3. 创建并行程序的思考过程
开发并行程序需要以下步骤:
- 识别可并行执行的工作:找到可以同时进行的计算部分。
- 划分工作和数据:将任务和数据分配到多个处理器上。
- 管理数据访问、通信和同步:确保数据一致性和任务协调。
主要目标:
- 加速(Speedup):通过并行化减少总执行时间。
- 高效率:优化成本、功耗等资源利用。
- 解决更大规模问题:利用多台机器的资源。
4. 并行算法/并行化的元素
一个有效的并行算法需要考虑以下元素:
- 并发任务:可以同时执行的任务。
- 任务到处理器的映射:如何将任务分配到多个处理器上。
- 数据分布:输入、输出和中间数据如何在处理器间分配。
- 共享数据访问管理:如何避免数据竞争和冲突。
- 处理器同步:在并行执行过程中,如何协调各处理器的执行。
最终目标:
- 最大化并发性:尽可能多地同时执行任务。
- 减少并行化开销:降低通信、同步等带来的额外开销。
5. 寻找并发工作:分解技术
分解是将计算任务分解为更小的子任务的过程,这是并行化的核心。文章介绍了多种分解技术:
5.1 Amdahl定律
Amdahl定律指出,并行化的最大加速比受限于程序中无法并行化的部分。如果程序中有S比例的串行部分,则最大加速比为:
Speedup ≤ 1/S
例子:假设一个程序中有10%的代码无法并行化(S = 0.1),即使有无限多的处理器,最大加速比也只能是10倍。
5.2 分解技术
以下是几种常见的分解技术:
5.2.1 递归分解
递归分解适用于分治策略的问题。将问题分解为子问题,递归地分解直到达到合适的粒度。
例子:快速排序(Quicksort)
- 将数组分为两部分,围绕一个基准值(pivot)进行划分。
- 每个子数组可以独立排序,进一步递归分解。
- 这种分解方式自然适合并行化,因为每个子数组的排序可以并行执行。
5.2.2 数据分解
数据分解基于数据的分布来推导任务。它通常分为输入数据分解、中间数据分解和输出数据分解。
Owner-Computes规则:分配给特定数据项的进程负责所有与之相关的计算。
例子:矩阵乘法
- 问题:计算两个矩阵A和B的乘积C。
- 分解方法:
- 输出数据分解:将矩阵C划分为四个子矩阵,每个子矩阵的计算独立于其他子矩阵。
- 输入数据分解:将矩阵A和B划分为子矩阵,每个子矩阵的乘法可以独立进行。
- 中间数据分解:在计算过程中,将中间结果分解为多个任务,进一步并行化。
具体分解:
假设矩阵C划分为四个子矩阵:
- 任务1:计算C1,1 = A1,1 × B1,1
- 任务2:计算C1,2 = A1,2 × B2,1
- 任务3:计算C2,1 = A2,1 × B1,2
- 任务4:计算C2,2 = A2,2 × B2,2
这种分解方式可以显著提高并行性。
5.2.3 领域分解
领域分解将计算领域划分为多个任务,每个任务负责其领域分区的计算结果。它通常用于矩阵问题或网格计算。
例子:定积分计算
- 问题:计算定积分 ∫01(x2+1)dx\int_{0}^{1} (x^2 + 1) dx∫01(x2+1)dx。
- 分解方法:将积分区间[0, 1]划分为多个子区间(如[0, 0.5]和[0.5, 1]),每个子区间由一个任务独立计算。
- 任务1:计算 ∫00.5(x2+1)dx\int_{0}^{0.5} (x^2 + 1) dx∫00.5(x2+1)dx
- 任务2:计算∫0.51(x2+1)dx\int_{0.5}^{1} (x^2 + 1) dx∫0.51(x2+1)dx
- 最后将两个任务的结果相加,得到最终结果。
5.2.4 探索性分解
探索性分解用于搜索解空间的问题,通常与执行过程相结合。这类问题涉及对状态空间的探索,每个状态可以视为一个独立任务。
例子:15拼图问题
- 问题:找到从初始状态到目标状态的解路径。
- 分解方法:从初始状态生成多个后续状态,每个后续状态作为独立任务进行探索。
- 任务之间可能有依赖关系,但可以通过并行化加速搜索过程。
5.2.5 推测性分解
推测性分解适用于任务之间的依赖关系未知的情况。它分为保守方法和乐观方法:
- 保守方法:仅在确定任务独立时才分配任务,可能导致并行性不足。
- 乐观方法:即使任务可能出错,也提前调度任务,需要回滚机制。
例子:离散事件模拟
- 问题:模拟一个网络中的事件,如节点延迟、队列大小等。
- 分解方法:每个事件可以独立处理,但事件之间可能存在依赖关系。
- 乐观调度:假设事件独立,提前处理,如果发现依赖关系导致错误,则回滚。
6. 总结
文章的核心内容包括:
- Amdahl定律:并行化的最大加速比受限于程序中的串行部分。
- 并行程序的开发:分解任务、分配工作、协调执行和映射到硬件。
- 分解技术:递归分解、数据分解、领域分解、探索性分解和推测性分解。
未来关注点:
- 识别依赖关系:减少串行部分,提高并行性。
- 优化局部性:减少通信开销。
- 减少同步:降低同步带来的性能损失。
以下是对《Parallel Computing Basics 2》讲义的详细总结,重点介绍了任务依赖图、任务交互图、任务特性、映射技术等内容,并通过具体例子帮助理解。
《2》
1. 并行算法与任务分解
并行算法的核心在于将计算任务分解为多个子任务,并通过合理映射到处理器上实现加速。本讲义重点讨论任务分解、任务依赖关系、任务交互以及任务到处理器的映射技术。
1.1 任务依赖图(Task-Dependency Graph)
任务依赖图是一个有向无环图(DAG),用于表示任务之间的依赖关系。某些任务只有在其他任务完成后才能开始执行,例如生产者-消费者关系。
关键概念:
- 并发度(Degree of Concurrency):可以同时执行的任务数量。通常关心平均并发度。
- 关键路径(Critical Path):图中最长的顶点加权路径,权重表示任务大小。关键路径长度决定了程序在并行执行时的最短执行时间。
例子:假设有一个数据库查询任务分解,任务依赖图如下:
Task 1 → Task 2 → Task 3
→ Task 4 → Task 5
- 并发度:最大并发度为2(Task 1执行时,Task 2和Task 4可以并行执行)。
- 关键路径长度:Task 1 → Task 2 → Task 3,假设每个任务执行时间为10个时间单位,则关键路径长度为30个时间单位。
1.2 任务交互图(Task-Interaction Graph)
任务交互图描述了任务之间的数据交互模式,通常包含任务依赖图作为子图。任务之间可能没有依赖关系,但仍然需要交互(例如共享数据访问)。
例子:稀疏矩阵-向量乘法
- 每个结果向量的元素计算可以视为独立任务。
- 如果向量b在任务间分配,则任务交互图与矩阵A的结构图相同(非零元素表示交互)。
2. 并行性能的限制
通过细化任务粒度可以提高并发度,但存在以下限制:
- 计算粒度的下限:某些计算无法进一步分解。例如,矩阵-向量乘法中,最多只能有 (n^2) 个并发任务。
- 通信开销:任务粒度越细,通信开销越大。粒度与开销之间的权衡决定了性能边界。
3. 映射技术
任务分解后,需要将任务映射到处理器上。合理的映射策略对并行性能至关重要。
3.1 映射的目标
- 最小化并行执行时间:
- 将独立任务映射到不同处理器。
- 尽早将关键路径上的任务分配到可用处理器。
- 将交互密集的任务映射到同一处理器,减少通信开销。
例子:数据库查询任务的映射
- 假设任务依赖图分为多个层级,每层的任务之间没有依赖关系。
- 将每层的任务分配到不同处理器,确保负载均衡。
3.2 映射的复杂性
合理的映射需要考虑以下因素:
- 任务依赖图和交互图:确保负载均衡和最小化通信。
- 任务生成方式:静态(已知)或动态(运行时生成)。
- 任务大小和数据大小:是否均匀、是否已知。
- 交互模式:静态或动态、规则或不规则。
4. 任务特性
任务的特性对并行算法的选择和性能有重要影响。
4.1 任务生成
- 静态任务生成:任务在运行前已知,适用于矩阵运算、图像处理等规则问题。
- 动态任务生成:任务在运行时生成,例如游戏中的15拼图问题,每次移动生成新的任务。
4.2 任务大小
- 均匀任务大小:所有任务执行时间相同,适合同步执行。
- 非均匀任务大小:任务执行时间不同,需要动态负载均衡。
例子:假设任务执行时间如下:
- Task 1: 10个时间单位
- Task 2: 20个时间单位
- Task 3: 5个时间单位
- 使用主从(Master-Worker)模式可以动态分配任务,避免负载不均衡。
4.3 数据大小
任务关联的数据大小可能影响性能,例如缓存效果和数据交换开销。
例子:稀疏矩阵-向量乘法
- 如果每个任务只处理少量数据,通信开销可能超过计算收益。
- 如果任务数据量大,任务可能绑定到特定处理器,难以动态迁移。
5. 任务交互特性
任务之间的交互模式对并行实现有重要影响。
5.1 静态与动态交互
- 静态交互:任务及其交互在运行前已知,易于编码。
- 动态交互:交互时机无法预知,需要动态检查(例如轮询)。
5.2 规则与不规则交互
- 规则交互:交互模式固定,易于实现。
- 不规则交互:交互模式复杂,难以优化。
例子:稀疏矩阵的不规则交互图
- 每个任务的邻居数量不同,交互模式难以预测。
5.3 交互类型
- 只读交互:任务只读取其他任务的数据。
- 读写交互:任务读取并修改其他任务的数据,需要额外同步。
6. 映射技术
映射是将任务分配到处理器的过程,目标是减少通信开销和空闲时间。
6.1 静态映射
静态映射在运行前完成,需要准确估计任务大小。常见的静态映射方法包括:
- 块分布(Block Distribution):将数据划分为块,分配到处理器。
- 循环分布(Cyclic Distribution):将任务按循环方式分配到处理器。
- 块循环分布(Block-Cyclic Distribution):将数据划分为更多块,按轮询方式分配。
例子:矩阵乘法的块分布
- 将矩阵A和B划分为子块,每个子块分配到一个处理器。
- 如果任务大小不均匀(例如LU分解),块循环分布可以减少负载不均衡。
6.2 动态映射
动态映射在运行时完成,适用于任务大小未知或动态生成的情况。
例子:15拼图问题
- 每次移动生成新的任务,动态分配到可用处理器。
7. 层次化映射
对于复杂任务图,单一映射技术可能不足。可以采用层次化映射:
- 顶层任务映射:将任务分解到顶层。
- 底层数据划分:在每个任务内进一步划分数据。
例子:快速排序的二叉树依赖图
- 顶层任务映射到超立方体结构,底层数据划分到每个任务内。
本讲义详细介绍了并行计算中的任务分解、任务依赖与交互、任务特性以及映射技术。通过合理分解任务、优化任务依赖关系、选择合适的映射策略,可以显著提高并行程序的性能。