呜~ 就隔了一段时间没看并行计算,发现作业贼难顶,不得不写篇博客来记录一下
复习(预习)的内容。
并行计算性能评测
并行机的一些基本性能指标
对并行计算机的性能关注点还是落在了CPU和存储器上,毕竟CPU和存储器决定了计算机处理问题速度的上限。
在https://www.top500.org/
收录了世界上前500台计算能力最强的计算机
可以看到,超算top 5领域美国就居了3个,我国神威·太湖之光排名4,看看神威的详细配置:
这么多核核内存orz… 不过好像处理器的频数并没有那么高,其实也就1.45GHz,个人笔记本都可以达到2.2GHz,台式机可以达到3.5GHz,或许是因为有千万个核,不需要太高的频率吧,再次Orx。后面是一些浮点运算计算能力,我后面会讲到。还有,这耗电也很厉害… 操作系统是Sunway RaiseOS
讲了top 500,还是回到标题的内容上来,评价一个并行机有哪些指标?下面是一些参考,有一些我们刚才已经在上面的神威中学习了。
上面mention到了顺序执行时间和并行执行时间,一般有下面的式子成立:
程序执行时间 = 计算时间 + 并行开销时间 + 相互通信时间
加速比性能定律
这个加速比其实我去年已经讲过了,在《计算机组成与设计》这里:https://blog.csdn.net/weixin_44026604/article/details/112167660,不过加速比可不仅仅是Amdahl定律
所谓并行加速比,其实是相对串行时间而言的,就是指:对于一个给定的应用,并行算法的执行速度相对于串行算法的执行速度加快了多少倍。
额,下面这些参数的意义得先有个了解才能继续往下聊:
- n: 并行系统中处理器数
- W: 问题规模(计算负载、工作负载,定义为给定问题的总计算量)
- Ws: 应用程序中的串行分量
- Wp: W中可并行化部分(Ws+Wp=W)
- f: 串行分量比例
- f =Ws/W
- Ts: 串行分量的执行时间
- Tp: 并行分量的执行时间
- S: 加速比
- E: 效率
Amdahl定律
Amdahl定律的出发点是在工作负载变化固定的情况下,增加处理器的个数来提高计算的速度,定律表达为:
上面这个式子就表示了:加速比不会随着处理器个数的无限增加而无限提高,而是会达到一个上限,这个上限是串行占比的倒数。一个程序,如果其串行分量越大,则其并行所获得加速比上限就越低;如果串行分量越小(意味着可并行的分量比例越大),则其并行所获得的加速比上限(天花板)就越高。
从上面的图中可以很清楚地看到,即使串行部分在程序中仅占了4%,但加速比的上限也就只能达到31了。所以,平时编OpenMP程序,运行时间与处理器核数不成正比也是可以理解的,毕竟一般程序都含有串行部分,相当于是一次函数,而不是正比例函数。
如果再考虑一个并行开销,则Amdahl公式变为:
Gustafson定律
Gustafson的出发点和Amdahl不同:Gustafson认为在固定的时间内,完成的事情越多,加速比越高。除非学术研究,在实际应用中没有必要固定工作负载而使计算程序运行在不同数目的处理器上。Gustafson定律认为,增多处理器必须相应地增大问题规模才有实际意义,公式表达为:
这个表达式看上去要比Amdahl乐观,它表示了:计算负载增加的情况下,相应的增加处理器的数量,所获得加速比也是在增加的。且从表达式看出,这个加速比似乎没有天花板?!!orz
当串行只占了4%左右,加速比保持在很高水平,如果再考虑一个并行开销:
有关加速的讨论
一般的经验高速我们:
-
可达线性加速的应用问题
矩阵相加、内积运算等,此类问题几乎没有通信开销 -
可达p/logp加速的应用问题
分治内的应用问题,类似于二叉树,树的同级可并行执行,但向根逐渐推进时,并行度将逐渐减少 -
超线性加速:由于高速缓存和内存的增加
-
绝对加速:最佳串行算法所用的时间除以同一问题其并行算法所用的时间
-
相对加速:同一算法在单处理器上运行的时间除以在多个处理器上运行的时间
基准测试程序
top 500的ranking可不是主观评出来的,而是程序跑出来的,测试计算机性能的程序一般称基准测试程序。了解一下就好:
基本测试程序
综合型基准测试程序Whetstone
- 为不同的计算机浮点性能而设计的综合型基准测试程序
- 即包括整数运算,又包括浮点运算,涉及数组下标索引、子程序调用、参数传递、条件转移和三角/超越函数等
综合型基准测试程序Dhrystone
- 为测试整数与逻辑运算性能而设计的综合型基准测试程序
- 一种CPU密集型测试程序
标准基准测试程序SPEC
- 主要是测试CPU性能的
- 强调开发能反映真实应用(如实际负载)的基准测试程序,并已推广到客户-服务器计算、商业应用、I/O子系统等
数学库测试程序
基准测试程序LinPACK
- 用全精度64位字长的子程序求解100阶线性方程组的速度
- 测试的结果以MFLOPS作单位给出
- 作用BLAS1的第一个线性代数软件包
基准测试程序LAPACK
- 使用了数值线性代数中最新、最精确的算法
- 采用了将大型矩阵分解成小块矩阵的方法,从而可有效地使用存储器
- 建立在BLAS1、 BLAS2和BLAS3基础上
基准测试程序ScaLAPACK
- 是LAPACK的增强版 ,主要为可扩放的、分布存储的并行计算机而设计的
并行测试程序
NAS Parallel Benckmark
- 1991年美国NAS项目所开发的并行测试程序
- 其目的是为了比较各种并行机性能
- 系由8个程序组成,测试范围从整数排列到复杂的数值计算
PARKBENCH
- 在1992年超级计算会议上确定的项目
- 主要目标是确定并行机用户与厂商双方都能接受的、内容丰富的一批并行测试程序及标准
- 4类PARKBENCH
- 底层基准程序
- 核心基准程序
- 密集应用基准程序
- HPF编译基准程序
【本章小结】掌握Amdhl定律和Gustafson定律,理解其计算基本的出发点,了解并行计算机常见的性能指标和测试程序
并行算法与并行计算模型
并行算法概述
算法表达
算法复杂性
这里的复杂度不仅仅是时间或者空间复杂度,正是由于并行算法运行在多核上的特殊性,因此其复杂性指标多,举一些来介绍:
- 运行时间
t(n)
- 算法运行在给定模型上求解问题所需的时间,包含计算时间和通信时间,分别用计算时间步和选路时间步作单位
- 处理器数
p(n)
- 求解给定问题所用的处理器数目
- 并行算法成本
c(n)
- c(n)=t(n)p(n)
- 成本最优
- 总运算量
W(n)
- 并行算法所完成的总的操作数量
同步与通信
同步就是多处理器之间要相互协作,在一些具有先后次序的问题上需要等待。譬如下面的求和算法:
通信简单理解就是数据交换。
并行计算模型
PRAM模型(PRAM:并行随机访问存储器)
特点:
- 具有有限个或者无限个功能相同的处理器
- 一个容量无限大的共享存储器
- 在单位时间内,每个处理器能访问任一存储单元
- 所有处理器同步执行PRAM指令(某些处理器可以空闲)
- 一个算法的运行时间是指令周期数
根据处理器对共享存储单元同时读、同时写的限制,PRAM模型又可分为:
- 不允许同时读和同时写(PRAM-EREW)
不允许两个处理器在同一时间访问同一存储单元,但允许访问不同的存储单元 - 允许同时读不允许同时写(PRAM-CREW)
- 允许同时读和同时写,但需要解决写冲突(PRAM-CRCW)
下面是一个运行在PRAM模型上的求和算法:
内层循环是可并行的,因此这个求和并行算法的时间t(n)=O(logn)
,处理器数p(n)=n/2
,成本c(n)=O(nlogn)
,总运算量W(n)=n-1
,加速比S(n)=O(n/longn)
。
PRAM模型的优点:
- 适合于并行算法的表达、分析和比较
- 使用简单,隐含了通信和同步等细节
- 易于设计算法,稍加修改便可运行在不同的并行计算机上
- 可推广,加入一些诸如同步和通信等需要考虑的问题
PRAM模型的缺点:
- 所有的指令均按锁步方式操作,很费时
- 共享单一存储器的假定不适合于分布存储的MIMD机器
- 假设每个处理器均可在单位时间内访问任何存储单元而略去存取竞争和有限带宽等是不现实的
APRAM模型(异步PRAM模型)
特点:
- 由p个处理器组成,每个处理器有其局部存储、局部时钟和局部程序
- 处理器之间的通信需要经过共享全局存储器
- 无全局时钟,各处理器异步独立执行各自的命令
- 处理器间任何时间依赖关系需要明确地在各处理器地程序中加入同步路障
- 一条指令可在非确定但优先的时间内完成
- 局部操作:单位时间
- 全局读和全局写:时间为d,d随p的增加而增加
- 同步:时间为B,是p的非降函数
- 一般有 2 ≤ d ≤ B ≤ p
计算过程:
- 计算由同步障分开的全局相组成
- 在各全局相内,每个处理器异步执行
- 在同一相内不允许两个处理器访问统一存储单元
- 运行时间为:(tph为全局相内各处理器指令执行时间中的最长者)
下面是一个运行在APRAM模型上的求和算法:
- 各处理器先求n/p个数的局部和,然后通过树自底向上逐层求和
- 运行时间
t(n) =
- 处理器数
p(n) =
- 成本
c(n) =
- 加速比
S(n) =
APRAM模型的优点:
- 比起PRAM更接近于实际的并行计算机
- 保留了PRAM编程的便捷性
- 使用了同步障,确保程序正确
- 成本参数定量化,易于分析算法
APRAM模型的缺点:
- 与实际的并行计算机仍然相差较远
- 不适合于消息传递并行计算机
BSP模型(下面两个模型简单了解一下就可以了)
结构:
- 处理器/存储器模块(处理器数为p)
- 施行处理器/存储器模块对之间点到点传递消息的选路器(吞吐率为g)
- 路障同步器(同步时间为L)
计算过程:
- 垂直结构:
- 局部计算
- 全局通信
- 路障同步
- 水平结构:
- p个处理器之间并发执行
- 一个超级步的运行时间
MAX(w+hg)+L
,w为执行局部计算的时间,h为发送/接收消息的数目
BSP模型的优点:
- 强调了计算和通信的分离
- 在一个超级步内,可将消息作为一个整体传递
- 易于分析算法复杂性
- 提供了一个用于编程的BSP函数库
BSP模型的缺点:
- 需要特殊的硬件支持全局路障同步,否则同步代价较大
LogP模型
一种分布存储的、点到点通信的多处理机模型,其中通信网络由一组参数来描述,但它并不涉及到具体的网络结构
参数说明:
- L表示在网络中消息从源到目的地所产生的延迟
- o表示处理器发送或接收一条消息所需的额外开销
- 在此期间内它不能进行其他操作
- g表示处理器可连续进行消息发送或接收的最小时间间隔
- g的倒数相应于处理器的通信带宽
- L和g反映了通信网络的容量
- p表示处理器/存储器模块数
- L、o和g都可以表示成处理器周期的整数倍
LogP模型的优点:
- 明确了通信网络的性能特征
- 隐藏了并行机的网络拓扑、路由、协议
- 可应用到共享存储、消息传递的编程模型中
LogP模型的缺点:
- 难以对算法进行描述、设计和分析
BSP模型和logP模型不同之处
- LogP基于成对消息传递,BSP进行整体通信
- LogP增加了一个参数o,表示传递消息的额外开销
- LogP鼓励计算与通信重叠,BSP强调计算与通信分离
常见问题的并行算法
向量运算
这个十分简单,一点依赖关系都没有,直接上OpenMP,串行的时间复杂度为O(n),并行时间复杂度为O(1),并行伪码为:
归约
在我看来,归约总是有点“归并”的味道,归约适用于求和、求最大值、计数等场景。下面是求总和的并行算法,时间复杂性为O(n),并行算法的复杂性为O(logn),所需的处理器数为n/2:
扫描
使用场景如求前缀和。下面是求前缀和的并行算法,串行时间复杂度为O(n),并行算法运行时间为O(logn),处理器数为n/2:
求前缀和也有另外的解法,这种解法处理器数为n,运行时间为O(logn)
矩阵相乘
矩阵相乘的串行算法时间复杂度为O(n³),算法可优化到O(n2.81),但使用并行,可以降到更低,下面是两种并行算法:
- 处理器数为n²,运行时间为O(n)
- 处理器数为n³,运行时间为O(logn)
无序数组的秩
所谓数组的秩,就是指定x,求数组中小于x的元素的个数,串行算法时间显然为O(n),并行算法为:
这里是把求无序数组A的秩,转化为求数组B的和,直接使用前面的求和并行算法就好。
处理器数为n/2,运行时间为O(logn)
有序数组的秩
串行可以使用二分,时间复杂度为O(logn)。一个简单的并行算法如下:
处理器数为n,运行时间为O(1)
若处理器数为根号n,则可以分段并行求解:
这样子的运行时间仍旧为O(1)
归并
所谓归并,就是指将两个有序数组合并为一个整体有序数组,这里为了方便,在原先的两个数组末尾加上了哨兵,这两个哨兵都是正无穷。下面是归并的两种并行算法:
并行算法1
这个算法的示意图:
核心是分段,分段进行排序,使用秩保证了分段之后的数字序列可以直接合并。
这个算法的处理器数为p,平均运行时间为O(n/p)
并行算法2
先引入一个定义,由这个定义所引出来的一个定理可以帮助我们归并序列:
双调序列:与单调序列相对,双调序列允许一个序列先增后降,或者先降后增(注意这里允许首尾相接),也就是下面几种情况都是属于双调序列
Batcher定理
由此我们可以设计以下算法:先将序列分为小序列和大序列,这样子就保证了小序列中的每个数都比大序列中的任意一个数小,那么采用递归方法就可以完成归并。
以上算法适用于数组个数为2k的情形
如果数组个数大小不是2k,则并行算法改为:
算法首先将原来两个有序数组赋值到一个新数组c里面(这里还强制把数组长度变成2的幂次方),然后调用前面的双调归并算法。这种情况下所需要的处理器数m+n,运行时间是O(log(m+n))
插入排序
我们发现,插入排序最终其实就是将每个数放到它的秩的位置上,其实不仅是插入排序,其他排序也可以利用这个思想。这种情形下的处理器数为n,运行时间为O(n)
tips:冒泡排序没法并行化
选择排序
处理器数为n²,运行时间为O(logn)
归并排序
前面我们已经讲了归并,知道了一般情形下归并的时间复杂度为O(logn),则按照归并排序的算法思想,易得归并排序的算法时间复杂度为O(log²n),处理器数为n/2。伪码如下(其实和串行伪码是一样的,只不过加上了并行)
快速排序的并行化
自上而下构造一棵二叉排序树,中序遍历可得到一个有序序列
- 初始化
root=i,选择根节点,每个处理器将自己的处理器号写入变量root,根据CRCW模型原理,最终只有一个处理器号会被写入变量root
f[i] = root,设置所有节点的父亲为根节点
LC[i] = RC[i] = n,设置所有节点的左右孩子为空
- 并行构造树的第二层
每个Pi比较a[i]
和a[f[i]]
的大小
<a[f[i]]
的节点竞争成为f[i]
的左孩子
>a[f[i]]
的节点竞争成为f[i]
的右孩子
竞争失败的节点将其父亲指向胜利者 - 循环构造树的第三层…
构造一级树的时间:O(1)
平均树高:O(logn)
平均时间复杂度:O(logn)
并行中序遍历平均时间:O(logn)
PSRS排序算法
PSRS假设只有p个处理器,(p远小于要排序的数组的元素数),这个算法是怎么做的呢?
这个算法的示意图如下:
这里假设了数组大小为27,一共有3个处理器
多路归并,既可以使用串行归并,也可以使用并行归并