News

Date: 2021-11-30来源:沐曦光启智能研究院科学家 李兆石

前情提要:


上一篇“软件到硬件,天堑变通途”中,我们从计算系统中间层(indirection)出发,给出了编程模型的两种定义:广义的编程模型指从应用到芯片之间的所有中间层;狭义的编程模型指从应用层到微架构层中,上层的哪些行为合法,以及每个行为在下一层的执行机制。每个中间层的引入都会对应用在芯片上的性能造成影响。因此,如果我们要充分开发应用在芯片上的性能潜力,就需要仔细反思每层编程模型的设计动机。本篇将以演化论的视角追溯硬件约束对编程模型的影响。


03. 三堵高墙


吉恩·阿姆达尔(Gene Amdahl)因其1967 年提出的“Amdahl 定律”举世闻名:随着线程数的增加,并行计算性能边际效用递减。但 Amdahl 于同年提出了第二条原则,有时被称为“Amdahl’s Rule of Thumb”或“Amdahl的另一条定律”:硬件架构设计需要平衡算力、内存带宽和 I/O 带宽,理想的系统,处理器计算性能与内存带宽与 I/O 带宽的比例为 1:1:1,即每 1 MIPS(million instructions per second,百万条指令数每秒) 的处理器计算性能需要 1 MB 的内存和 1 Mbps 的 I/O 带宽。


1638246766371471.png

图1 1980年至2020年间CPU 计算性能、内存带宽、磁盘带宽和网络带宽随时间的变化(数据来源[1])。
当硬件性能错位的张力无法在之前的体系结构-编程模型的设计中得以解决时,计算系统遇到了内存墙、功耗墙和 I/O 墙


“Amdahl 的另一条定律”在提出时被视为金科玉律,是可以写在1990年第一版《量化研究方法》的扉页上的定律。但到今天它已经鲜为人知。这其中的关键在于,计算系统的内存带宽与 I/O 带宽的比例自1985 年以后,因为集成电路工艺的限制,就已经无法与芯片算力维持在 1:1:1 的理想比例了。


如图1 所示,CPU 计算性能、内存带宽、磁盘带宽和网络带宽的增速错落有致。如同地壳运动中两个板块间的错位会形成悬崖峭壁,计算系统中不同模块的性能错位的张力也会形成一堵堵高墙。


集成电路诞生六十年后,产业界和学术界公认有三座高墙:1995 年后内存性能和CPU 性能错位形成的内存墙(memory wall),2005 年后 CPU 性能和芯片功耗错位形成的功耗墙(power wall),和 2015 年后CPU 性能和 I/O 带宽错位形成的 I/O 墙(I/O wall)。


为了越过三堵高墙,维持一个平衡的系统,芯片架构、编程模型、编译器和软件工程的研发人员使出浑身解数,设计出了很多复杂的机制。其中有些机制,可以完全不破坏先前的编程模型,仅仅通过硬件机制来实现,如多级高速缓存、乱序超标量处理器架构等;而另外的机制,则如上一篇结尾所言,需要以编程模型的形式暴露给架构设计师、编译设计师或者应用开发者。这就对应了上篇结尾的编程模型三种技术路线。虽然所有编程模型的设计目标都是方便程序员开发利用底层硬件机制,但因为硬件机制的复杂性,对应的编程模型也是芜杂多样,难以一言以蔽之。


因此,为了理解繁杂的编程模型,我们必须追根溯源,以一种演化论和整体论的视角分析各种编程模型诞生时的硬件约束和软件约束。这样,我们才能在约束发生改变之际,避免落入“祖宗之法不可变”的窠臼;而是通过调整编程模型适应变化。本篇关注硬件约束,即所谓的“三堵高墙”。下一篇将关注软件约束。


番外一:从计算机体系结构反思理性建构主义


Was vernünftig ist, das ist wirklich; und was wirklich ist, das ist vernünftig.

(凡是合乎理性的东西都是现实的;凡是现实的东西都是合乎理性的。)


——黑格尔《法哲学原理》


黑格尔的话简称“黑话”。这句话中的“现实”、“理性”都跟我们日常用语含义不同。本系列可以看作后半句的一个注解:通过分析现实中存在的种种硬件机制和编程模型的设计动机,尝试用理性整体地把握异构编程模型的设计脉络,最终目的是为了在未来的编程模型演化中适应硬件和软件约束的变化。


但是这句话的后半句非常频繁地被断章取义后广泛误解,并被当作名言警句误传于大众话语圈。通常的表述形式有:存在即合理;甚至干脆就是“凡是存在的都是正当的”。误传的关键在于,汉语中“合理”一词具有歧义:英文的“合乎理性(Rational)”/“正当性(Righteous)”都被译成“合理”。于是,黑格尔的后半句话就沦落为大众偷懒不作为的借口。


更危险的误解是对前半句的一种浪漫阐释:合乎理性的都将变成现实。这种误解将人类(最初误入歧途的是青年黑格尔派)引向理性建构主义(constructivist rationalism):如果人类自己创造了社会和文明中的各种制度,那么,人类为了满足自己的欲望或需求,肯定也能够根据自己的意愿去改变它们(引自哈耶克)。这种人类对理性的自负,结合人类对乌托邦的向往,给人类带来了巨大的灾难。


我个人之所以喜爱计算机体系结构,一个原因是我认为计算机体系结构是完全由人类理性(特指logos,逻辑,秩序;而非nous,经验,智慧)设计出的最复杂的造物之一。也因此,它能帮我们反思人类logos的有限性。


对于我等凡人而言,只要接触到并行编程,感受到被多核处理器上debug多线程程序所支配的恐惧时,内心中一定会充斥着对自己的logos的鄙夷(怎么看起来这么简单的多生产者-消费者队列都搞不定......),然后转头去调用各路大神debug好的多线程安全 (thread-safe) 数据结构库。


然而即使超越普通人,转而去观察人类logos的结晶,体系结构天才们的logos也不过尔尔,依然难以保证其造物不出bug。例如,当主流处理器设计从片上单核转向片上多核后,缓存连贯性模型由Total Store Ordering切换到Release Consistency,层出不穷的遗留代码中的bug在最近十年令学术界和工业界焦头烂额;“幽灵 (Specter)”和“熔断 (Meltdown)”等处理器硬件安全漏洞在2017年后被大量发现,而这些漏洞的来源,正是在1990年代,体系结构研究者们引以为傲的分支预测和推测执行技术。


而在社会制度中,logos建构主义给社会带来的bug,远比计算机体系结构的bug恐怖。启蒙时代理性主义代言人伏尔泰的一句名言点明了logos建构主义的方法论:“欲求良律,焚旧而立新可矣”。人类对logos的盲信,妄图依赖logos建构一套社会理论,一举解决当下所有社会问题,然后因此造就了一个个恐怖的bug。斯宾塞、严复等人的社会达尔文主义思想要对二十世纪上半叶几乎所有的悲剧负责,马尔库塞等人的激进主义思想则要对二十世纪下半叶几乎所有的悲剧负责。社会不能依靠logos建构。可是人类对乌托邦的向往一次又一次地重复错误。


如果有可能,应该让所有的政治家去体验一下形式化验证,让他们意识到要验证他们logos中的A和现实中的B完全相同,是一件多么困难的事情。只可惜文科理科之间“道不同不相为谋”的难题似乎无解,而这也是人类理性之傲慢的必然结果。


3.1 冯·诺依曼架构


为了理清编程模型的演化脉络,本小节将回溯经典的冯·诺依曼架构,开始溯源之旅。


最初的计算机仅仅装载具有固定用途的程序,其硬件由各种门电路构成。一个特定的程序通过由这些门电路组装成的一个固定电路板来执行。因此,如果需要修改程序功能,则必须重新组装电路板。


1945年,冯·诺依曼提出了计算机“存储程序”的计算机设计理念,其基本思想为将计算机指令进行编码后存储在计算机的存储器中,即存储程序型计算机。通过将指令当成一种特别类型的静态数据,一台存储程序型计算机可轻易改变其程序,并能够在程序控制下改变其工作任务。这就是冯·诺依曼计算机体系的开端。


这种设计理念导致了软、硬件的分离,从而催生了程序员这一职业。同时,利用将指令当成数据这一概念,使得汇编语言、编译器及其他自动编程工具得以实现,并孕育了编程模型的雏形。此外,借助“自动编程的程序”,即编译器,程序员可以以人类较易理解的方式编写程序。


图2展示了冯·诺伊曼架构的结构图。冯·诺依曼的论文确定了“计算机结构”中的 5 大部件:运算器、控制器、存储器、输入设备、输出设备。从此以后, 运算器和控制器单元集成在处理器中实现,存储器的容量不断扩大,输入输出设备不断更新。这些基本部件的演进过程,正是现代计算系统的发展历程。然而,后续出现的处理器、内存和外设在演进中的性能错位,迫使人类设计出了越来越复杂的硬件机制和编程模型。


1638247027883986.png

图2 冯·诺伊曼结构的设计概念


冯·诺依曼架构对应的编程模型是随机存取机模型。在随机存取机模型下,应用程序的执行状态和数据保存在一个处理器内的有限数量的寄存器、以及外部的存储器中。处理器中的寄存器仅仅保存应用程序执行的中间状态。所有的数据最终都要体现在存储器中。本系列将在第3篇从随机存取机模型出发回溯“程序”这一最基本的软件概念。


3.2 内存墙


1638247139790513.png

图3  DRAM中一个1-bit数据单元的示意图。数据存储在CStorage中,受读/写晶体管的控制,经过放大器读出数据


自1958 年罗伯特·诺伊斯和杰克·基尔比发明集成电路以后,冯·诺依曼架构中的各个部件开始逐渐被集成电路取代。首先是处理器。然后是存储器。早期的磁性存储器无法用集成电路替代,直到1965 年 IBM 发明了基于集成电路的动态随机访问存储器(dynamic random access memory, DRAM),存储器才被集成电路所取代。1965 年的摩尔定律和1974 年的Dennard定律为集成电路的发展制定了路线图。因为处理器和存储器都适用于摩尔定律,“Amdahl 的另一条定律”也得到了保证。在很长一段时间内,计算系统的性能跟着摩尔定律大步前进。


然而,盛世之下,危机潜藏。由于晶体管级电路设计的局限性,DRAM 的读延迟率先跟不上摩尔定律的步伐。如图3所示,DRAM 存储器中存储 1 bit 数据的单元由一个电容器和一个晶体管组成。电容器用于存储数据,晶体管用于控制电容器的充放电。读数据时,晶体管被选通。电容器上存储的电荷将非常轻微地改变源极的电压。之后,感测放大器(sense amplifier)可以检测到这种轻微变化,该结构将电压微小的正变化放大到高电平(代表逻辑 1),并将电压微小的负变化放大到低电平(代表逻辑 0)。感测过程是一个缓慢的过程。该感测时间决定了 DRAM 的访问时间。因此,DRAM 访问时间的缩小速度远远落后与摩尔定律下处理器两次访存间时间间隔的缩小速度。这就是冯·诺依曼体系结构发展中遇到的内存墙。


1638247268766469.png

图4 以1980年的性能为基准,处理器的性能(处理器两次访存间的时间间隔)和DRAM访存延迟间的差距逐渐拉大。
然后在2005年左右,随着处理器性能受限于功耗,差距又逐渐缩小(图来源于[1])


图4清晰地展示出了内存墙问题。如果每一次访存都需要花费几十到几百个周期等待存储器的响应,处理器的性能提升会变得没有意义。为了解决这一问题,计算机体系结构研究中铸造了两把利剑:高速缓存(cache)和内存级并行(memory-level parallelism, MLP)。


Cache 利用局部性原理,减少了 CPU 访问主存的次数。简单地说,处理器正在访问的指令和数据,可能会被以后多次访问到;或者是该指令和数据附近的内存区域,也可能会被多次访问。因此,在第一次访问这一块区域时,将其复制到处理器芯片上的cache 中,以后再访问该区域的指令或者数据时,就不需要再访问芯片外部的存储器了。加入 cache 后,冯·诺依曼架构中的存储器变成了一种层次化的存储结构。


Cache 对于编程模型而言是一个完全透明的部件。虽然程序员可以根据缓存的特点对程序代码实施特定优化,从而获得更好的性能,但程序员通常无法直接干预对缓存的操作。因此,在编程模型设计空间探索中,一般不会考虑 cache 的问题 。


MLP 指的是处理器同时处理多个内存访问指令的能力。在层次化存储结构的cache和DRAM 之间,以及 DRAM 的多个存储体(bank)之间,多个来自处理器的访存请求可以并发处理。例如,当访存指令在cache 中未命中,等待 DRAM 中的数据时,若后续访存指令在 cache 中命中,可以先完成后续访存指令,从而避免处理器阻塞在少数延迟很大的未命中访存指令上。MLP 虽然不能减少单个操作的访问延迟,但它增加了存储系统的可用带宽,从而提高系统的总体性能。


实现 MLP 时,硬件开发者设计了多线程并发执行、指令多发射和指令重排序等硬件机制。他们的目的都是引入多个并发的、没有依赖关系的访存指令,从而开发MLP。但他们与编程模型之间的相互作用就远比cache复杂。本系列将于第4篇再详细介绍为了提供更多MLP,硬件机制和编程模型设计中做出了的努力。


3.3 功耗墙


摩尔定律保证了单个晶体管的速度呈指数上升,以及面积和造价指数下降。随着单个芯片上集成的晶体管数量越来越多、密度越来越大,芯片制造时必须要考虑散热的问题。1974年,Robert Dennard提出,当芯片的尺寸缩小S倍,频率提升S倍,只要芯片的工作电压相应地降低S倍,单位面积的功耗就会保持恒定。图 5展示了Dennard缩放是如何确保芯片单位面积的功耗恒定的。在新一代工艺下,单位面积的晶体管数量会增大S2倍,频率提升S倍,单位面积的功耗却能保持不变。在Dennard缩放的担保下,Intel等芯片制造公司可以快速通过提高芯片的工作频率,同时利用更多的晶体管提供更复杂的功能,而不需要考虑芯片的散热问题。从1971年的Intel 4004到2006年的Intel core 2处理器,芯片的工作电压从15V逐渐下降到了1V左右。


1638247398215570.png

图5 Dennard缩放因为阈值电压的缘故于2006年左右终结。图中S是两代半导体工艺之间的缩放因子,
一般而言,S=1.4,即下一代工艺单个晶体管的面积是上一代的1/2(长和宽各缩小为上一代的1/1.4),性能是上一代的1.4倍,电容是1/1.4


但在2005年,芯片的工作电压已经降低到0.9V左右,非常接近晶体管的阈值电压(0.4V~0.8V)。受限于晶体管的材料和结构,阈值电压很难进一步降低,因此芯片的工作电压也无法再降低。从此,芯片的尺寸每缩小一半,单位面积的功耗将提高一倍。更糟糕的是,当工作电压接近阈值电压时,晶体管栅极到衬底之间的漏电功耗在总功耗中的占比也越来越大。新一代半导体工艺将面临芯片单位面积功耗提升的挑战。这就是芯片的“功耗墙”问题。


为了克服功耗墙的问题,2005年以后的芯片设计中普遍采用了“暗硅”(dark silicon)的思路:通过多核和异构架构等设计,限制了芯片上全速工作(点亮)的工作区域,从而使得芯片满足了功耗约束。图6展示了Intel Skylake架构的处理器版图。以Skylake的版图为例,能将实现“暗硅”的硬件机制分为三类:


2-6.png

图6 Intel Skylake架构的处理器版图。单个芯片上有4个CPU核和一个GPU。在任何给定的时间点,只有一部分的电路在工作,从而满足了功耗的约束。


  1. 增加低频率模块,如设计更大的cache、更多的单指令流多数据流(single-instruction-stream-multiple-data stream, SIMD)执行单元等。这里的频率,即指工作频率,又指使用频率。以cache为例,“暗硅时代”的CPU上有至多接近一半的面积是用来实现cache的(图6中CPU缓存包括最末级缓存(last-level cache,LLC )和每个CPU Core中的L1和L2 cache)。一方面,由于MLP的存在,cache的工作频率可以低于处理器的数据通路,如Intel SandyBridge以前的架构中,L3 cache与内核的电压、频率可以单独控制。另一方面,由于CPU的cache由许多块SRAM构成,当前没有被访问到的SRAM的控制逻辑,可以通过门控时钟(clock gating)等技术降低功耗。通过增加cache来降低硬件频率的机制对编程模型完全透明。


  2. 增加并行的硬件模块。根据图5的Dennard缩放原理,当先进工艺下晶体管的尺寸缩小为上一代工艺的1/2时,如果单个处理器内核的频率保持不变,则它的面积缩小为原来的1/4,功耗(由于单个晶体管的电容减小)缩小为原来的1/2。这样,如果芯片的功耗预算不变,我们可以在芯片上再增加三个较低频率的内核。同时,由于在任何给定的时间点,只有一部分的CPU核在工作,所以芯片可以通过门控时钟和门控电压(power gating)等技术关闭部分内核,进一步降低功耗。现实中,自2005年以后,x86架构处理器不再专注于提高芯片的频率,而是在新的芯片中增加芯片上处理器内核的数量。ARM架构的BIG-LITTLE架构更是在一个芯片上同时放置高性能BIG核和高能效LITTLE核,充分利用了并行硬件模块机制带来的设计空间。但是,为了充分利用芯片的性能,应用开发者不得不学习多线程编程的技巧。基于多线程的并行化机制使得硬件的性能难以被应用开发者充分开发。


  3. 定制化硬件。通用处理器为了确保其通用性,会有大量的冗余。2010年一篇ISCA论文在通用处理器上运行H.264解码后发现,处理器的执行单元消耗的能量仅占总能量的5%,大量的能量被消耗在了处理器的取指、译码等模块上。因此,在一块芯片上根据应用需求设计定制化硬件模块,进而构成异构片上系统(heterogeneous System-on-Chip, SoC),可以极大地提高其能量效率。图6中的Skylake架构处理器上集成的GPU就是专门为处理图像渲染定制的。此外,Intel SSE/AVX等SIMD运算单元也可以看作一种定制化硬件。SIMD运算单元采用一个控制器来控制多个运算器,同时对一组数据向量中的每一个分别执行相同的操作,从而在这组数据向量上摊薄了处理器的取指、译码的开销。当然,SIMD单元仅仅适用于存在非常规整的数据级并行的场景。定制化硬件和异构片上系统的普及带来了编程模型的“巴别塔难题”:不同的定制化硬件模块需要不同的编程模型;同样的应用运行在不同的定制化硬件上时,需要花费大量的人力对应用进行“翻译”。


“功耗墙”难题虽然没有停下摩尔定律的脚步,但却摧毁了冯·诺伊曼架构上的随机存取机编程模型。由于片上多处理器(chip multi-processor, CMP)和异构架构逐渐成为主流的硬件设计方法,应用开发者不得不被迫直面并行编程模型和异构编程模型。


正是在这一时期,GPU及GPGPU编程模型因为提供了远比CPU高的单位算力能量效率,在计算产业崭露头角。本系列将在第5篇开始,详解GPGPU编程模型。


3.4 I/O墙


冯·诺依曼架构可以分为存储、计算和外设三大功能模块。之前的两次危机,分别由于存储速度与计算速度不匹配、计算功耗受限造成的。与此同时,计算机教材中一直给学生灌输,外设速度是远远慢于计算速度的。然而,从2015年左右开始,这一延续了几十年的教条逐渐开始失效。如今,外设速度与计算/存储速度的不匹配,正在形成一道新的“I/O”墙。


2010年以后网络的带宽飞速增长;2015年以后硬盘的速度飞速增长。而同时期CPU-DRAM的带宽增长速度远远滞后于网络和硬盘的增长速度。造成这一现象的主要原因有两个:(1) 内存墙、功耗墙的先后出现,使得CPU-DRAM间带宽的增长速度远远落后于摩尔定律的步伐。当前主流的CPU-DRAM间的DDR接口,带宽大约每5~7年提升一倍。(2) 网络和硬盘在物理层的技术突破,使得它们的带宽爆炸式增长。21世纪的头十年,主流的网络传输介质的铜双绞线,主流的硬盘存储介质是磁盘。2010年之后,数据中心光通信模块开始普及,相比基于铜双绞线的网卡,光纤网络单卡的带宽从1Gbps快速提升到数十Gbps。2015年以后,SSD开始取代HDD。之前HDD使用机械马达在磁碟上寻址。受限于磁碟的转速(大约15000转每分钟),单个HDD的带宽最高只能达到数百MB/s。但闪存技术的发展,尤其是闪存的写入持久性 (persistence) 的进步,使得SSD开始取代HDD。SSD的寻址过程类似DRAM,完全由电信号驱动,因此带宽不再受到寻址速度的限制。


时至今日(2021年),关于如何解决I/O墙问题依然是一个开放问题。学术界和工业界都提出了大量的研究和解决方案。大多数研究和解决方案都在尝试让冯·诺依曼架构走向更加异构的方向:为网络和存储增加计算的功能,从而将原本CPU的计算卸载到外设上进行。


网络和存储两个方向上的探索,被学术界统一概括为软件定义网络/存储。具体而言,软件定义网络将网络协议栈,例如TCP/IP协议、TLS协议等,以及一些底层的数据访问API,卸载到DPU/IPU等网络领域定制芯片上进行处理;软件定义存储将存储器的一些需求,如日志记录、RAID、压缩等,卸载到靠近存储器的运算单元上进行处理。软件定义网络、软件定义存储与软件定义芯片的结合,必将在未来十年大放异彩。


番外二:光刻墙与芯粒技术


感谢你能坚持到本篇的结尾,下面再给出一个原书没有的小彩蛋。


对于GPGPU等面积巨大的芯片而言,另一堵由于半导体工艺约束而产生的高墙已经近在咫尺:光刻墙,指的是光刻设备在一次曝光中能支持的最大芯片面积 (reticle limit)。当前主流的EUV光刻机只能支持到858平方毫米。更大的芯片面积会由于需要多次曝光,芯片良率会急速下滑。除非像Cerebras那样有DARPA和DOE在背后做金主的公司,商业芯片公司不会选择让芯片面积超过Reticle limit。


然而目前GPGPU的芯片面积已经非常接近Reticle limit。NVIDIA A100单个芯片的面积是826平方毫米。如果想要更多的晶体管,从而提供更多的计算资源和更复杂的硬件机制,未来GPGPU必须要像AMD EPYC处理器那样走向单封装集成多芯粒 (Chiplets) 的技术路线。而多芯粒的架构演化路线,势必将对现有编程模型产生巨大的冲击。


本篇我们完成了对硬件演化历程的反思。下个月我们将对软件的演化历程进行反思。欢迎大家三连走起(点赞,在看,关注),这样Marcom小姐姐们更有动力催稿,然后我才更有动力去啃范畴论(200多页的书,我在最近两周每天看也只啃了16页)写下一篇。


参考资料


[1] John Hennessy, David A. Patterson, Computer Architecture: A Quantitative Approach, 6th edition.

[2]  魏少军,刘雷波,朱建锋,邓辰辰,《软件定义芯片》,2021年9月。

[3]  https://www.zhihu.com/question/19851152

  • China Business CooperationBusiness@metax-tech.com
  • International Business CooperationInternational.Business@metax-tech.com
  • Media CooperationPR@metax-tech.com