经验分享谈谈几个高性能并行计算中的复杂度概念
Posted 极智视界
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了经验分享谈谈几个高性能并行计算中的复杂度概念相关的知识,希望对你有一定的参考价值。
欢迎关注我的公众号 [极智视界],回复001获取Google编程规范
大家好,我是极智视界,本文介绍一下几个高性能并行计算中的复杂度概念,主要包括计算复杂度、访存复杂度和指令复杂度。
下面开始。
文章目录
1、计算复杂度:
不管是怎样的算法,我们都可以把算法封装成一个函数,该函数带有若干输入、若干输出、若干计算。更进一步说,这个过程基本会涉及数据加载指令、运算指令、数据存储指令和其他若干指令。
为了进一步泛化,假设有如下符号变量:ni 表示第 i 种指令的使用频次,Ti 表示第 i 种指令的吞吐量的倒数(当处理器是吞吐量优化的设备,采用指令吞吐量;当处理器是延迟优化的设备,采用指令时钟周期),因此计算复杂度的数学表达式如下:
此外,在高性能并行计算上应用 计算复杂度 分析时,若控制流之间的计算量并不均衡,那么需要同时考虑控制流之间的 最大计算复杂度 和 最小计算复杂度,用它们的比例就可大致估计 负载不均衡的程度,进而估计优化负载及最大可提升的性能比例。
假设一个有着两个线程的并行算法,两个线程的计算复杂度分别为 O(0.5n) 和 O(2n),此时计算时间由 O(2n) 来决定,应用负载均衡算法后,最好的性能提升为 2*2/(2+0.5)=1.6
倍。
2、访存复杂度
把访存复杂度单独拿出来的目的是为了分析算法中访存方面是否可继续优化。
对于某一个具体的程序来说,通常有以下几种带宽定义:
(1) 存储器的峰值带宽 Pb:该值由硬件规格决定,通常可由硬件参数计算出来。如双路双通道 DDR3-2333 内存,其峰值带宽为:2(双路) × 2(双通道) × 2.333(内存等效频率) × 64/8(64位内存控制器) = 74.6 GB/s;又比如 RTX2080 的显卡带宽,其峰值带宽为:14(访存速度) × 256(位宽) / 8 = 448 GB/s。
(2)可获得的存储器带宽 Ab:该值亦由硬件决定,不过其大小由程序测试获得。由于硬件设计方面可能存在不足,可获得的存储器带宽通常要小于存储器的峰值带宽。如 NVIDIA K20c GPU 的峰值显存带宽大约 200GB/s,但是可获得的显存带宽大约 175 GB/s。
(3)程序发挥的存储器带宽 Eb:该值为某个特定程序发挥出来的硬件带宽,一般而言,此值可通过硬件计数器获得,但是某些顶级的软件开发人员也能够手工计算出来。
(4)程序的有效访存带宽 Vb:该值由算法计算获得,表示程序最小要访问的数据量与程序计算时间的比值,还可表示访存优化能够达到的上限,对于程序访问带宽的优化不可能比程序的有效访存带宽还小。
访存复杂度分析的是程序发挥的存储器带宽,因为其值是程序在硬件的存储器上执行时的 实际访存字节数 。
可获得的存储器带宽与存储器的峰值带宽的比例可以反映存储器处理存储器访问的效率,比如 Intel Xeon Phi 处理器的峰值显存带宽约为 350 GB/s,而可获得的显存带宽约为 170 GB/s,这就是一个典型的存储器硬件存在不足的例子。对于主流的 GPU 而言,其值在 70% ~ 80%,而在 Intel X86 处理器上,其值通常能够超过 90%。
程序发挥的存储器带宽与可获得的存储器带宽的比例可以表示程序已经发挥了硬件能够提供的带宽的程度,可以通过它来分析存储器还能够提供的带宽余量,如果值接近 1,则表示存储器的带宽已经被耗尽,下一步的优化应当考虑 如何减少访存次数 及 被浪费的存储器带宽。
程序的有效访存带宽和程序发挥的存储器的比例表示程序访问存储器的效率,称之为 访存效率。程序的访存效率越高表示浪费的带宽越小,即访存模式越优秀。比如访存效率为 50%,表示有一半的带宽访问是程序不需要的。
为了更好的说明以上四个带宽定义的约束关系,总结为如下的数学关系式子:
即对于某个算法而言,可能会存在如下以下四种情况,下表罗列了对应的说明和解决办法:
(Vb/Eb) / (Eb/Ab) | 低 | 高 |
---|---|---|
低 | 硬件带宽跟得上,算法可以优化 | 硬件带宽跟得上,算法最好优化,可以减少带宽利用率。也就是说这个时候纯粹就是代码效率不高。 |
高 | 硬件带宽跟得上,算法已经无需优化 | 物尽其用没有进一步优化空间了。如果还觉得速度不够快,那么就只能换设备。 |
也就是说,我们经常所说的带宽受限的情况基本是在 Eb/Ab 比较高的情况;而我们最需要解决是 Vb/Eb 低而 Eb/Ab 高的情况,这个时候的代码优化空间很大。
3、指令复杂度
指令复杂度乍看跟计算复杂度是重复概念,其实不然,此时需回到并行计算的本身进行解释。众所周知,并行计算中最小粒度的并行发生在指令层面,使用高级语言开发的用户是难以察觉的,因为高级语言在经过编译器编译成机器码后,其指令逻辑和结构和原始指令已经大相径庭。
相比计算复杂度和访存复杂度,指令复杂度则分析实现算法的某个具体程序生成的指令数量、类型及 是否可以同时执行等。也就是说,指令复杂度意在分析 算法执行时指令间 的并行性,这种并行性在计算复杂度中是无法体现的。
我们知道 GPU 是以吞吐量优化为主的设备,CPU 是以延迟优化为主的设备。所以对于 CPU 而言,尽量减少指令使用频次,即可提高程序效率;而对于 GPU,需要尽可能在代码中提供这些特点:数据依赖少、指令密集、并行度高。此外,我们应该意识到 吞吐量 和 延迟 不一定是单纯的倒数关系,因为流水线、乱序执行、多线程等因素,大大影响了吞吐量和延迟之间的关系。下面进行举例说明:
上图中,指令的延迟时钟周期为 4,按照吞吐量的定义,其吞吐量为 (1/一时钟周期)。假设单指令的多过程不为 4,而是 10,那么延迟时钟周期为 10,但吞吐量仍旧为 (1/一时钟周期)。上述情况是在单发射指令的情况下得到的,若一个时钟周期内可以发射 n 条指令,即指令多发射,此时的吞吐量为 n。在没有任何数据依赖、寄存器依赖的情况下,吞吐量不会受到延迟的影响。
再来讨论一下为何 GPU 能够高效处理计算密集型程序,而无法高效处理复杂逻辑计算的原因。假设在多指令发射处理器中,多发射指令个数为 m,单发射指令的多过程数为 n,执行 k 条指令的 总时钟周期 和 吞吐量 如下,其中 T0 为一个时钟周期:
上述公式能够获得接近 m 的吞吐量,主要原因是相连的指令可以高度并行、没有数据依赖,这些特征尤其在计算密集型程序中更为明显,这也是为何 GPU 这种以吞吐量优化为主的处理器适合进行高性能并行计算的原因。而 CPU 之所以适合处理逻辑复杂计算,主要是因为 CPU 以延迟优化为主,每个指令的延迟周期很短,不同指令随意组合后的整体程序执行时间延迟差别不会太大。
以上分享了几个高性能并行计算中的复杂度概念,希望我的分享能对你的学习有一点帮助。
【公众号传送】
扫描下方二维码即可关注我的微信公众号【极智视界】,获取更多AI经验分享,让我们用极致+极客的心态来迎接AI !
以上是关于经验分享谈谈几个高性能并行计算中的复杂度概念的主要内容,如果未能解决你的问题,请参考以下文章