摘要
构建软件的并行版本可使应用在更短的时间内运行指定的数据集,在固定时间内运行多个数据集,或运行非线程软件禁止运行的大型数据集。 并行化的成功通常通过测量并行版本的加速(相对于串行版本)来进行量化。 除了上述比较之外,将并行版本加速与可能加速的上限进行比较也十分有用。 通过阿姆达尔定律和古斯塔夫森定律可以解决这一问题。
本文是“英特尔多线程应用开发指南”系列的一部分,该系列介绍了针对英特尔® 平台开发高效多线程应用的指导原则。
背景
应用运行的速度越快,用户等待结果所需的时间越短。 此外,执行时间的缩短使用户在可接受的时间内能够运行更大规模的数据集(例如,更多的数据记录,更多的像素,或更大的物理模型)。 串行与并行执行时间之间一个具体的比较指标便是加速比(speedup)。
简单来说,加速比是串行执行时间与并行执行时间的比率。 例如,如果串行应用运行需 6720 秒,对应的并行应用运行需 126.7 秒(使用 64 个线程和内核),则并行应用的加速比是 53X (6720/126.7 = 53.038)。
对于扩展良好的应用,加速比增加的速度应与内核(线程)数量增加的速度相同或接近。 当增加使用的线程数时,如果测量的加速比不能维持不变或开始下降,那么就测量的数据集,该应用的扩展性不够理想。 如果该数据集是典型的实际数据集,而应用将在此之上执行,那么该应用的扩展性能则不理想。
与加速比相关的另一个指标是效率(efficiency)。 正如加速比是衡量并行执行比串行执行快多少的指标,效率表示的是软件对系统计算资源的利用程度。 要计算并行执行的效率,只需将观察到的加速比除以使用的内核数, 然后将得到的数值以百分数表示即可。 例如,加速比为 53X, 使用 64 个内核,那么效率就等于 82% (53/64 = 0.828)。 这意味着,在应用执行过程中,平均每个内核大约有17% 的时间处于闲置状态。
阿姆达尔定律
在启动一个并行化项目前,开发人员会希望预估他们能够实现的性能提升量(加速比)。 如果知道(或预估出)能够以并行方式执行的串行代码的百分数,那么开发人员可使用阿姆达尔定律计算应用的加速比上限,无需实际编写任何并发代码。 本系列介绍了阿姆达尔定律公式的几种变形。 每种变形均使用并行执行时间 (pctPar) 、串行执行时间 (1 - pctPar) 和线程/内核 (p) 的百分数(建议)。 下面是一个简单的阿姆达尔定律公式,用于评估基于 p 个内核上并行应用的加速比。
该公式只是串行时间(标准化为 1)与预估的并行执行时间的简单相除,使用标准化的串行时间的百分数。 并行执行时间表示为串行执行的百分数 ( 1 - pctPar)加上能够以并行方式执行的百分数与所用内核数 ( pctPar/p) 的除数。 例如,如果 95% 的串行应用运行时间可以在 8 个内核上以并行方式执行,根据阿姆达尔定律,预估的加速比等于 6X (1 / (0.05 + 0.95/8)= 5.925)。
除了在公式中的小于或等于关系 (=),阿姆达尔定律公式假设这些能够以并行方式执行的计算可被无限内核数整除。 这一假设实际消除了分母中的第二项,意味着最大的加速比即是剩余串行执行百分数的倒数。
因为忽略了实际开销,例如通信、同步和其它线程管理,以及无限内核处理器的假设,阿姆达尔定律一直饱受批评。 除了没有考虑并发算法固有的开销,对阿姆达尔定律最强烈的批评之一是,随着内核数量的增加,处理的数据量也可能会增加。 阿姆达尔定律假设不论内核数量如何,数据集大小均为固定,并且整体串行执行时间保持不变。
古斯塔夫森定律
如果使用 8 核的并行应用能够计算的数据集是原始大小的 8 倍,串行部分的执行时间会增加吗? 即使有增加,它也并非与数据集的增加同比例增长。 实际数据显示串行执行时间几乎保持不变。斯塔夫森定律又被称为扩展的加速比(scaled speedup),它考虑了数据大小与内核数量成比例的增加并计算应用的加速比(上限),假设大数据集能够以并行方式执行。 扩展的加速比公式如下:
与阿姆达尔定律公式相同, p代表内核数量。 为简化表述,对于指定的数据集大小, s 代表并行应用中的串行执行时间的百分数。 例如,如果在 32 个内核上 1% 的执行时间用于串行执行,对于同一数据集,基于单个内核和单个线程运行的应用的加速比是:
现在来考虑阿姆达尔定律基于这些假设估计的加速比。 假设串行执行的百分比是 1%,阿姆达尔定律等式得出 1/(0.01 + (0.99/32)) = 24.43X。 这是个错误计算,因为给定的串行时间百分数与 32 内核执行有关。 该示例没有指出对于更多或更少的内核(甚至只有一个内核),对应的串行执行百分数将是多少。 如果代码扩展完美,并且数据大小与内核数同时扩展,那么该百分数能够保持不变,阿姆达尔定律计算的结果将是 32 内核上(固定大小)单核问题的预测加速比。
另一方面,如果在 32 内核的案例中知道总的并行应用执行时间,则可以计算全部串行执行时间,并且针对固定大小问题的加速比(进一步假设该值可以使用单核计算)可以通过阿姆达尔定律基于 32 内核进行预测。 假设在 32 内核上并行应用的总执行时间是 1040 秒,则该时间的 1% 是串行执行时间,或 10.4 秒。 乘以 32 内核上并行执行的秒数 (1029.6),该应用完成总工作量所花时间为 1029.6*32+10.4 = 32957.6 秒。 非并行时间(10.4 秒)是总工作时间的 0.032%。 使用该数字,阿姆达尔定律计算出的加速比为 1/(0.00032 + (0.99968/32)) = 31.686X。
运用斯塔夫森定律时,必须知道并行执行期间串行时间的百分数,因此该公式的一个典型用例是计算扩展的并行执行(数据集大小随着内核数量的增加而增加)与相同大小问题串行执行的加速比。 从上面的示例可以看出,由于在阿姆达尔定律的公式中有关应用执行数据的严格使用,得出的估值比扩展的加速比公式得出的值悲观得多。
建议
在计算加速比时,必须对最佳的串行算法和最快的串行代码进行比较。 通常,非最佳串行算法将更容易并行化。 即便如此,虽然有更快的串行版本,但也不是所有人都会使用串行代码。 因此,即使底层算法不同,必须使用最快串行代码中的最佳串行运行时间来计算可比较并行应用的加速比。
在说明加速比时,应使用乘数值。 过去,加速比一直以百分数表示。 在本文中,使用百分数会引起困惑。 例如,如果说并行代码比串行代码快 200%,那么它的运行时间是串行版本时间的一半,还是该时间的三分之一? 105% 的加速比是几乎与串行执行时间相同还是比串行执行时间快两倍? 基准串行时间是 0% 加速比还是 100% 加速比? 另一方面,如果并行应用的加速比是 2X,很显然它使用一半的时间(即,并行版本在相同的时间内能够执行两次,而串行代码执行一次)
在极少数情况下,应用的加速比大于内核数。 这种现象被称为超级线性加速。 发生超级线性加速的典型原因是固定大小数据集被分解得足够小(对内核而言),可以放入本地高速缓存。 当以串行方式运行时,数据必须通过高速缓存获取,在获取期间处理器只能等待。 如果数据足够大,需占用清空之前使用的某些高速缓存行,那么后续对这些高速缓存行的任何复用都会导致处理器再次等待。 当数据被分解成可放入内核上高速缓存的数据块时,一旦这些数据被全部存入高速缓存,则无需经历复用高速缓存行所带来的等待复用。 因此,使用多个内核可以消除在单个内核上与串行代码执行相关的一些系统开销。 这样,过小的数据集(小于一般的数据大小)便会产生性能提升的错觉。
使用指南
此外还有其它并行执行模型尝试对阿姆达尔定律简单模型中的缺陷给出合理假设。然而,因为其简单性和用户理解这只是理论上限(几乎不可能达到或超越),所以阿姆达尔定律仍是表示串行应用加速比潜力的一项简单、有用的指标。
更多资源
John L. Gustafson。 "重新评估阿姆达尔定律," 《美国计算机学会通讯》第 31 卷,第 532-533 页,1988 年。
Michael J. Quinn。 《利用 MPI 和 OpenMP 的 C 并行编程》. McGraw-Hill,2004 年。