目录
5 Thread-Level Parallelism
5.1 Introduction
5.5 Synchronization: The Basics
5.6 Models of Memory Consistency: An Introduction
5.7 Cross-Cutting Issues
5.8 Putting It All Together: Multicore Processors and Their Performance
5.9 Fallacies and Pitfalls
5.10 The Future of Multicore Scaling
5.11 Concluding Remarks
6 Warehouse-Scale Computers to Exploit Request-Level and Data-Level Parallelism
6.1 Introduction
6.2 Programming Models and Workloads for Warehouse-Scale Computers
6.3 Computer Architecture of Warehouse-Scale Computers
6.4 The Efficiency and Cost of Warehouse-Scale Computers
6.5 Cloud Computing: The Return of Utility Computing
6.6 Cross-Cutting Issues
6.7 Putting It All Together: A Google Warehouse-Scale Computer
6.8 Fallacies and Pitfalls
6.9 Concluding Remarks
7 Domain-Specific Architectures
7.1 Introduction
7.2 Guidelines for DSAs
7.3 Example Domain: Deep Neural Networks
7.4 Google’s Tensor Processing Unit, an Inference Data Center Accelerator
7.5 Microsoft Catapult, a Flexible Data Center Accelerator
7.6 Intel Crest, a Data Center Accelerator for Training
7.7 Pixel Visual Core, a Personal Mobile Device Image Processing Unit
7.8 Cross-Cutting Issues Heterogeneity and System on a Chip (SOC)
7.9 Putting It All Together: CPUs Versus GPUs Versus DNN Accelerators
7.10 Fallacies and Pitfalls
7.11 Concluding Remarks
5 Thread-Level Parallelism
对传统组织结构的转变发生在20世纪60年代中期,当时在提高计算机操作速度的努力中,收益递减法则开始显现……电子电路的操作速度最终受到光速的限制……许多电路已经在纳秒范围内运行。
——W. Jack Bouknight 等,《Illiac IV 系统》(1972)
我们将所有未来的产品开发都专注于多核设计。我们相信这是行业的一个关键转折点。
——英特尔总裁保罗·奥特林尼,在2005年英特尔开发者论坛上描述英特尔的未来方向
自2004年以来,处理器设计师增加了核心数量,以利用摩尔定律的扩展,而不是专注于单核性能。丹纳德缩放失效,部分是对向多核部件转变的响应,可能很快会限制多核扩展,就像单核扩展已被遏制一样。
——Hadi Esmaeilzadeh 等,《功率限制与暗硅挑战多核的未来》(2012)
5.1 Introduction
正如本章开头的引用所示,一些研究人员已经认为单处理器架构的进展即将结束。这种看法显然是过早的;实际上,在1986年至2003年期间,由微处理器驱动的单处理器性能增长达到了自20世纪50年代末和60年代初第一台晶体管计算机以来的最高水平。尽管如此,随着设计师寻求一种方法来构建能够实现比单个微处理器更高性能的服务器和超级计算机,同时利用商品微处理器的成本效益优势,多处理器的重要性在90年代不断增长。正如我们在第1章和第3章中讨论的,由于在利用指令级并行性(ILP)方面收益递减的放缓,加上对功耗日益严重的担忧,导致了计算机架构进入了一个新时代——一个多处理器从低端到高端都扮演重要角色的时代。第二个引用准确捕捉了这一明显的转折点。
多处理器重要性增加反映了几个主要因素:
- 在2000年至2005年间,设计师试图发现和利用更多ILP时遇到的硅和能源使用效率显著降低,结果证明这种做法是低效的,因为电力和硅的成本增长速度快于性能。除了ILP,我们所知道的唯一可以快速提高性能的方法(从切换的角度来看)是通过多处理。
- 随着云计算和软件即服务变得越来越重要,对高端服务器的兴趣日益增长。
- 数据密集型应用的增长,受益于互联网上海量数据的可用性。
- 对桌面性能的提升关注减少(至少在图形之外),要么是因为当前性能可以接受,要么是因为高度计算和数据密集型的应用正在云端进行。
- 对如何有效使用多处理器有了更好的理解,尤其是在存在大量数据集(通常以数据并行性形式)、科学和工程代码中的“自然世界”并行性或大量独立请求之间的并行性(请求级并行性)等显著固有并行性的服务器环境中。
- 通过复制而不是独特设计来利用设计投资的优势;所有多处理器设计都提供这样的杠杆作用。
第三个引用提醒我们,多核可能仅提供有限的性能扩展可能性。阿姆达尔法则的影响与丹纳德缩放的结束意味着,多核的未来可能受到限制,至少在单个应用程序的性能扩展方面。我们将在本章后面再次讨论这个主题。
在本章中,我们重点关注利用线程级并行性(TLP)。TLP意味着存在多个程序计数器,因此主要通过多指令多数据流(MIMD)来利用。尽管MIMD已经存在了几十年,但线程级并行性在从嵌入式应用到高端服务器的各类计算中走到前沿是相对较新的现象。同样,广泛应用于各种通用应用程序的线程级并行性,相较于事务处理或科学应用,也较为新颖。
我们在本章的重点是多处理器,我们将其定义为由紧密耦合的处理器组成的计算机,这些处理器的协调和使用通常由单一操作系统控制,并通过共享地址空间共享内存。这种系统通过两种不同的软件模型利用线程级并行性。第一种是执行一组紧密耦合的线程协同完成单一任务,通常称为并行处理。第二种是执行多个相对独立的进程,这些进程可能源自一个或多个用户,这是一种请求级并行性,尽管其规模远小于我们在下一章探讨的内容。
请求级并行性可以通过在多个处理器上运行的单一应用程序来实现,例如数据库响应查询,或通过多个独立运行的应用程序来实现,通常称为多程序设计。
我们在本章考察的多处理器通常从双处理器到数十个甚至数百个处理器不等,通过共享内存进行通信和协调。虽然通过内存共享暗示了共享地址空间,但并不一定意味着只有一个物理内存。这类多处理器包括多个核心的单芯片系统,称为多核,以及由多个芯片组成的计算机,每个芯片通常也是多核的。许多公司制造这种多处理器,包括惠普、戴尔、思科、IBM、SGI、联想、甲骨文、富士通等。
除了真正的多处理器外,我们还将讨论多线程技术,该技术支持多个线程在单个多发射处理器上以交错方式执行。许多多核处理器也包含对多线程的支持。
在下一章中,我们将考虑由大量处理器构建的超大规模计算机,这些处理器通过网络技术连接(不一定是用于将计算机连接到互联网的相同网络技术),通常称为集群;这些大规模系统主要用于云计算,执行大量独立任务并行处理。最近,像搜索和某些机器学习算法等可以轻松并行化的计算密集型任务也利用了集群。当这些集群增长到数万台服务器及以上时,我们称之为仓库级计算机。亚马逊、谷歌、微软和Facebook都制造仓库级计算机。
除了我们在这里研究的多处理器和下一章的仓库级系统,还有一系列特殊的大规模多处理器系统,有时被称为多计算机。这些系统的耦合性通常低于本章讨论的多处理器,但通常高于下一章的仓库级系统。这类多计算机的主要用途是在高端科学计算中,尽管它们有时也用于商业应用,填补多处理器和仓库级计算机之间的空白。Cray X 系列和 IBM BlueGene 是这些多计算机的典型例子。
许多其他书籍,如 Culler 等(1999),详细介绍了这些系统。由于多处理领域的庞大和不断变化(刚提到的 Culler 等参考书超过 1000 页,仅讨论多处理!),我们选择将注意力集中在我们认为最重要和通用的计算空间部分。附录 I 讨论了在大型科学应用背景下构建此类计算机时出现的一些问题。
我们的重点将放在大约有 4 到 256 个处理器核心的多处理器上,这些处理器可能占用从 4 到 16 个独立芯片。这样的设计在单位数量和经济价值上都占据主导地位。在大规模多处理器中,互连网络是设计的关键部分;附录 F 专注于这一主题。
### 多处理器架构:问题与方法
为了利用具有 n 个处理器的 MIMD 多处理器,我们通常需要至少有 n 个线程或进程来执行;在大多数现代多核芯片中,利用多线程,这个数字通常会增加 2 到 4 倍。单个进程中的独立线程通常由程序员识别或由操作系统创建(来自多个独立请求)。在另一种极端情况下,一个线程可能由几十次循环迭代组成,这些迭代是由一个并行编译器生成的,利用了循环中的数据并行性。
尽管分配给线程的计算量(称为粒度)在考虑如何高效利用线程级并行性时非常重要,但与指令级并行性的一个重要定性区别在于,线程级并行性是在软件系统或程序员的高层次上识别的,且线程包含数百到数百万条可以并行执行的指令。
线程还可以用来利用数据级并行性,尽管其开销通常高于 SIMD 处理器或 GPU(见第 4 章)。这种开销意味着粒度必须足够大,以便有效地利用并行性。例如,尽管向量处理器或 GPU 能够有效地对短向量的操作进行并行化,但当并行性分布在多个线程之间时,结果的粒度可能会小到开销使得在 MIMD 中利用并行性变得过于昂贵。
现有的共享内存多处理器可以分为两类,具体取决于涉及的处理器数量,这又决定了内存组织和互连策略。我们根据内存组织来称呼多处理器,因为什么构成小数量或大数量的处理器随着时间的推移而不断变化。
第一组我们称之为对称(共享内存)多处理器(SMPs),或集中式共享内存多处理器,通常具有小到中等数量的核心,通常不超过32个。对于处理器数量如此之少的多处理器,处理器可以共享一个所有处理器都能平等访问的单一集中式内存,因此称之为对称。在多核芯片中,内存通常在核心之间以集中方式共享;目前大多数现有的多核处理器都是SMP,但并非所有。(注意,有些文献错误地将SMP用作共享内存处理器的缩写,但这种用法是不正确的。)
一些多核处理器对最外层缓存的访问是非均匀的,这种结构称为非均匀缓存访问(NUCA),因此即使它们有单一主内存,也不能真正称为SMP。IBM Power8具有分布式L3缓存,对L3中不同地址的访问时间是非均匀的。
在由多个多核芯片组成的多处理器中,通常每个多核芯片都有独立的内存。因此,内存是分布式的,而不是集中式的。正如我们在本章后面将看到的,许多采用分布式内存的设计对本地内存的访问速度快,对远程内存的访问速度却要慢得多;通常,各个远程内存的访问时间差异相对于本地内存和远程内存的访问时间差异来说是微小的。在这种设计中,程序员和软件系统需要意识到访问是针对本地内存还是远程内存,但可能能够忽略远程内存之间访问的分布。
由于随着处理器数量的增加,SMP方法变得不那么有吸引力,因此大多数大型多处理器使用某种形式的分布式内存。
SMP架构有时也被称为统一内存访问(UMA)多处理器,这源于所有处理器从内存访问的延迟是统一的,即使内存被组织成多个银行。图5.1展示了这些多处理器的外观。SMP的架构是第5.2节的主题,我们将在多核背景下解释这一方法。

图5.1 基于多核芯片的集中式共享内存多处理器的基本结构。多个处理器缓存子系统共享同一物理内存,通常在多核上有一个级别的共享缓存,以及一个或多个级别的每核心私有缓存。关键的架构特性是所有处理器对所有内存的访问时间是统一的。在多芯片设计中,互连网络将处理器和可能是一个或多个内存银行的内存连接起来。在单芯片多核中,互连网络仅是内存总线。

图5.2 2017年分布式内存多处理器的基本架构通常由一个多核多处理器芯片组成,该芯片附带内存和可能的I/O,以及一个与互连网络接口,连接所有节点。每个处理器核心共享整个内存,但连接到核心芯片的本地内存的访问时间将远快于对远程内存的访问时间。
另一种设计方法则是物理上分布式内存的多处理器,称为分布式共享内存(DSM)。图5.2展示了这些多处理器的外观。为了支持更多的处理器数量,内存必须在处理器之间分布,而不是集中;否则,内存系统将无法在不产生过长访问延迟的情况下支持更多处理器的带宽需求。
随着处理器性能的快速提升以及处理器内存带宽需求的增加,偏好使用分布式内存的多处理器规模不断缩小。多核处理器的引入意味着,即使是一些由2个芯片组成的多处理器(可能具有16到64个处理器核心)也采用分布式内存。处理器数量的增加也提升了对高带宽互连的需求,我们将在附录F中看到相关示例。无论是有向网络(即交换机)还是间接网络(通常为多维网状结构)都被广泛使用。
将内存分布在各个节点之间不仅增加了带宽,还减少了对本地内存的延迟。分布式共享内存(DSM)多处理器也被称为非统一内存访问(NUMA),因为访问时间取决于数据字在内存中的位置。DSM的主要缺点是处理器之间的数据通信变得稍微复杂,并且DSM需要更多的软件工作来利用分布式内存提供的增加内存带宽。由于大多数基于多核的多处理器系统使用分布式内存,因此我们将从这个角度解释分布式内存多处理器的操作。
在对称多处理(SMP)和分布式共享内存(DSM)架构中,线程之间的通信通过共享地址空间进行,这意味着任何处理器都可以引用任何内存位置,只要它拥有正确的访问权限。与SMP和DSM相关的共享内存术语指的是地址空间是共享的。
相比之下,下一章中的集群和大规模计算机看起来像是通过网络连接的独立计算机,一个处理器的内存无法在没有运行在两个处理器上的软件协议的帮助下被另一个处理器访问。在这样的设计中,使用消息传递协议在处理器之间传输数据。
### 并行处理的挑战
多处理器的应用范围从几乎没有通信的独立任务到需要线程间通信以完成任务的并行程序。两个重要的障碍可以用阿姆达尔法则来解释,这使得并行处理变得具有挑战性。克服这些障碍通常需要一个全面的方法,涉及算法及其实现、底层编程语言和系统、操作系统及其支持功能,以及架构和硬件实现。尽管在许多情况下,其中一个因素是关键瓶颈,但当处理器数量扩展到接近100个或更多时,往往所有软件和硬件的各个方面都需要关注。
第一个障碍与程序中可用的有限并行性有关,第二个障碍则源于通信的相对高成本。可用并行性的限制使得在任何并行处理器上取得良好的加速变得困难,正如我们的第一个例子所示。
**例子** 假设您想在100个处理器上实现80倍的加速。那么,原始计算中可以是顺序的部分占多少比例?
**答案** 回忆一下第一章,阿姆达尔法则是:

为了简化这个例子,假设程序仅在两种模式下运行:并行模式(所有处理器完全使用,即增强模式)和串行模式(仅使用一个处理器)。在这种简化情况下,增强模式下的加速简单地等于处理器的数量,而增强模式的比例则是花费在并行模式上的时间。将其代入前面的公式:

因此,要在100个处理器上实现80倍的加速,原始计算中只能有0.25%的部分是顺序的!当然,要实现线性加速(即用n个处理器获得n倍的加速),通常整个程序必须是并行的,没有任何串行部分。在实际应用中,程序并非仅在完全并行或串行模式下运行,而是在并行模式下通常使用的处理器数量少于全部处理器。阿姆达尔法则可以用来分析具有不同加速效果的应用,正如下一个例子所示。
**例子** 假设我们有一个在100个处理器的多处理器上运行的应用程序,并且假设该应用程序可以使用1、50或100个处理器。如果我们假设在95%的时间内可以使用所有100个处理器,那么在剩余的5%执行时间中,必须使用多少个处理器才能实现80倍的加速?
**答案** 我们使用阿姆达尔法则,考虑更多的项:

如果一个应用程序95%的部分可以完美地使用100个处理器,要实现80倍的加速,剩余时间中必须有4.8%用于使用50个处理器,而仅有0.2%的时间可以是串行的!
并行处理中的第二个主要挑战涉及远程访问的高延迟。在现有的共享内存多处理器中,数据在不同核心之间的通信可能需要35到50个时钟周期,而在不同芯片上的核心之间则可能需要从100个时钟周期到300个或更多的时钟周期(对于大规模多处理器),这取决于通信机制、互连网络的类型和多处理器的规模。长时间的通信延迟显然会产生显著影响。让我们考虑一个简单的例子。
**例子** 假设我们有一个在32个处理器的多处理器上运行的应用程序,该应用程序处理对远程内存的引用时有100纳秒的延迟。对于该应用程序,假设除了涉及通信的引用外,所有引用都命中本地内存层级,这显然是乐观的。处理器在远程请求上会被阻塞,处理器的时钟频率为4 GHz。如果基准CPI(假设所有引用都在缓存中命中)为0.5,那么如果没有通信,与0.2%的指令涉及远程通信引用相比,多处理器的性能提升多少?
**答案** 首先计算每条指令的时钟周期会更简单。对于具有0.2%远程引用的多处理器,其有效CPI为:

多处理器在所有本地引用的情况下比基准快1.3/0.5,即2.6倍。实际上,性能分析要复杂得多,因为一部分非通信引用会在本地层级中未命中,而远程访问时间也不是一个固定的常数。例如,远程引用的成本可能会更高,因为多个引用试图使用全局互连造成的竞争可能导致延迟增加,或者如果内存是分布式的并且访问的是本地内存,则访问时间可能会更好。这个问题也可以使用阿姆达尔定律进行分析,我们将这一练习留给读者。
这些问题——不足的并行性和长延迟的远程通信——是使用多处理器时面临的两个最大性能挑战。必须主要通过软件解决应用程序并行性不足的问题,采用提供更好并行性能的新算法,以及通过最大化使用完整处理器组执行的时间的软件系统来解决。减少长远程延迟的影响可以通过架构和程序员两方面来解决。例如,我们可以通过硬件机制(如缓存共享数据)或软件机制(如重构数据以使更多访问变为本地访问)来降低远程访问的频率。我们还可以通过使用多线程(本章后面讨论)或预取(我们在第2章中广泛覆盖的主题)来尝试容忍延迟。
本章的大部分内容集中在减少长远程通信延迟影响的技术上。例如,第5.2至5.4节讨论了如何利用缓存来减少远程访问频率,同时保持内存的一致视图。第5.5节讨论了同步问题,这因为它固有地涉及处理器间通信,并且可能限制并行性,因此是一个主要的潜在瓶颈。第5.6节涵盖了延迟隐藏技术和共享内存的一致性模型。在附录I中,我们主要关注用于科学工作的较大规模多处理器。在该附录中,我们研究了这类应用的性质以及在几十到几百个处理器上实现加速的挑战。
5.2 Centralized Shared-Memory Architectures
使用大型多级缓存可以显著减少处理器的内存带宽需求,这一观察是驱动集中式内存多处理器发展的关键洞见。最初,这些处理器都是单核的,通常占据整个电路板,内存则位于共享总线上。随着最近更高性能处理器的出现,内存需求超出了合理总线的能力,现代微处理器直接将内存连接到单个芯片上,这有时被称为背面或内存总线,以区别于用于连接I/O的总线。访问芯片的本地内存,无论是针对I/O操作还是来自其他芯片的访问,都需要经过“拥有”该内存的芯片。因此,内存访问是不对称的:对本地内存的访问更快,而对远程内存的访问则较慢。在多核处理器中,这些内存在单个芯片上的所有核心之间共享,但从一个多核处理器访问另一个多核处理器的内存时,通常仍然存在这种不对称的情况。
对称共享内存机器通常支持共享数据和私有数据的缓存。私有数据由单个处理器使用,而共享数据则被多个处理器使用,本质上通过读取和写入共享数据提供了处理器之间的通信。当私有项被缓存时,其位置会迁移到缓存中,从而减少平均访问时间以及所需的内存带宽。由于没有其他处理器使用该数据,因此程序行为与单处理器中的行为相同。当共享数据被缓存时,共享值可能会在多个缓存中复制。除了减少访问延迟和所需内存带宽外,这种复制还减少了可能因多个处理器同时读取共享数据项目而产生的竞争。然而,共享数据的缓存引入了一个新问题:缓存一致性。
### 什么是多处理器缓存一致性?
不幸的是,缓存共享数据引入了一个新问题。由于两个不同处理器对内存的视图是通过各自的缓存实现的,这些处理器可能会看到同一内存位置的不同值,如图5.3所示。这种困难通常被称为缓存一致性问题。注意,一致性问题的存在是因为我们同时拥有一个主要由主内存定义的全局状态和一个由每个处理器核心私有的各自缓存定义的局部状态。因此,在某个级别的缓存可能是共享的多核系统中(例如,L3),尽管某些级别是私有的(例如,L1和L2),一致性问题仍然存在并且必须解决。

图5.3 单个内存位置(X)的缓存一致性问题,由两个处理器(A和B)进行读写。我们最初假设两个缓存均不包含该变量,并且X的值为1。我们还假设使用的是直写缓存;而写回缓存则会增加一些额外但类似的复杂性。在A写入X的值后,A的缓存和内存都包含新值,但B的缓存不包含该值,如果B读取X的值,它将接收到1!
非正式地说,如果任何对数据项的读取返回该数据项最近写入的值,我们可以称内存系统是一致的。这个定义虽然在直观上很吸引人,但却模糊而简单;现实情况要复杂得多。这个简单的定义包含内存系统行为的两个不同方面,这两个方面对于编写正确的共享内存程序都是至关重要的。第一个方面称为一致性,定义了读取时可以返回哪些值;第二个方面称为一致性性,决定了何时会通过读取返回已写入的值。让我们先看一致性。
如果一个内存系统是一致的,则满足以下条件:
1. 处理器P在对位置X进行写操作后,随后对X进行读取时,如果在P的写操作和读取之间没有其他处理器对X进行写操作,则总是返回P写入的值。
2. 处理器对位置X的读取跟随另一个处理器对X的写入时,如果读取与写入在时间上有足够的间隔,并且在这两个访问之间没有对X的其他写入,则返回已写入的值。
3. 对同一位置的写入是串行化的;即,任何两个处理器对同一位置的两个写入在所有处理器中以相同的顺序被看到。
例如,如果值1然后值2被写入某个位置,处理器不能读到该位置的值为2,然后再读为1。第一个属性简单地保持程序顺序——我们期望即使在单处理器中这个属性也为真。第二个属性定义了何为一致的内存视图:如果一个处理器能够持续读取旧的数据值,我们显然会说内存是不一致的。
写入序列化的需求更为微妙,但同样重要。假设我们没有对写操作进行序列化,处理器P1先写入位置X,随后P2也写入位置X。序列化写入确保每个处理器都能在某个时间点看到P2的写入。如果不进行序列化,可能会出现某些处理器先看到P2的写入,然后再看到P1的写入,从而无限期地保持P1写入的值。避免这种困难的最简单方法是确保对同一位置的所有写入以相同的顺序被看到;这个特性称为写入序列化。
虽然刚刚描述的三个属性足以确保一致性,但何时能看到已写入值的问题也很重要。要理解这一点,注意我们不能要求读取X时立即看到由其他处理器写入的值。例如,如果一个处理器对X的写入在另一个处理器读取X之前只相差很短的时间,那么可能无法确保该读取返回写入的数据值,因为该数据甚至可能尚未离开处理器。读取器必须在何时看到已写入值的问题由内存一致性模型定义——这一主题将在第5.6节中讨论。
一致性和连贯性是互补的:连贯性定义了对同一内存位置的读取和写入的行为,而一致性则定义了对其他内存位置的读取和写入的行为。现在,我们做以下两个假设。首先,一个写入在所有处理器看到该写入的效果之前不会完成(并允许下一个写入发生)。其次,处理器不会改变任何写入与其他内存访问的顺序。这两个条件意味着,如果一个处理器先写入位置A,再写入位置B,则任何看到B的新值的处理器也必须看到A的新值。这些限制允许处理器重新排序读取,但强制处理器按照程序顺序完成写入。我们将依赖这个假设,直到第5.6节,在那里我们将看到这一定义的确切含义以及替代方案。
### 强化一致性的基本方案
对于多处理器和I/O的一致性问题,尽管源头相似,但由于其特性不同,因此适当的解决方案也有所不同。与I/O不同,后者多个数据副本是一种少见的事件——应尽量避免——而在多个处理器上运行的程序通常会在多个缓存中存在相同数据的副本。在一致性多处理器中,缓存提供了共享数据项的迁移和复制功能。
一致性缓存提供迁移,因为数据项可以被移动到本地缓存中,并以透明的方式使用。这种迁移减少了访问远程分配的共享数据项的延迟,以及对共享内存带宽的需求。
由于缓存会在本地缓存中复制数据项,一致性缓存还为同时读取的共享数据提供了复制功能。复制减少了访问延迟和对读取共享数据项的争用。支持这种迁移和复制对访问共享数据的性能至关重要。因此,多处理器并不试图通过软件避免这个问题,而是通过引入协议来维护一致性缓存,从而采用硬件解决方案。
维护多个处理器一致性的协议称为缓存一致性协议。实现缓存一致性协议的关键是跟踪任何数据块共享的状态。缓存块的状态通过与块关联的状态位来保持,这类似于单处理器缓存中的有效位和脏位。当前使用的协议可分为两类,每种类使用不同的技术来跟踪共享状态:
- **基于目录的协议**——特定物理内存块的共享状态保存在一个位置,称为目录。基于目录的缓存一致性有两种非常不同的类型。在对称多处理器(SMP)中,可以使用一个集中式目录,该目录与内存或其他单一的序列化点(例如,多核中的最外层缓存)相关联。在分布式共享内存(DSM)中,拥有一个单一目录是没有意义的,因为这会造成单点争用,并使得在多核心芯片的内存需求下难以扩展。分布式目录比单一目录更复杂,这种设计在第5.4节中讨论。
- **监听协议**——而不是将共享状态保存在单一目录中,每个拥有物理内存块数据副本的缓存可以跟踪该块的共享状态。在SMP中,缓存通常通过某种广播介质(例如,总线将每个核心的缓存连接到共享缓存或内存)可供访问,所有缓存控制器监控或监听该介质,以确定它们是否拥有总线或交换访问请求的块的副本。监听协议也可以作为多芯片多处理器的协调协议,一些设计支持在每个多核内的目录协议之上使用监听协议。
监听协议在使用微处理器(单核心)和通过总线连接到单个共享内存的缓存的多处理器中变得流行。总线提供了一个方便的广播介质来实现监听协议。多核架构显著改变了这一局面,因为所有多核都共享芯片上的某些级别的缓存。因此,一些设计转向使用目录协议,因为其开销较小。为了让读者熟悉这两种类型的协议,我们在这里重点介绍监听协议,并在讨论DSM架构时讨论目录协议。
### 监听一致性协议
有两种方法来维持前一节中描述的一致性要求。一种方法是在写入数据项之前,确保处理器对该数据项具有独占访问权限。这种协议被称为写入失效协议,因为在写入时会使其他副本失效。这是迄今为止最常见的协议。独占访问确保在写入发生时不存在其他可读或可写的副本:所有其他缓存的副本都会被失效。

图5.4 一个在窥探总线上工作的失效协议示例,涉及单个缓存块(X)和写回缓存。我们假设初始时没有任何缓存持有X,并且内存中X的值为0。在处理器和总线活动完成后,处理器和内存的内容显示了相应的值。空白表示没有活动或没有缓存副本。当处理器B发生第二次未命中时,处理器A用值响应,取消了来自内存的响应。此外,B的缓存内容和内存中X的内容都会被更新。这种在块变为共享时更新内存的方式简化了协议,但也可以跟踪所有权并强制写回,仅在块被替换时才进行。这需要引入一个额外的状态位,以指示块的所有权。所有权位表示一个块可能被共享用于读取,但只有拥有该块的处理器可以写入该块,而该处理器负责在更改或替换块时更新其他处理器和内存。如果多核使用共享缓存(例如L3),那么所有内存都通过共享缓存进行访问;在这个例子中,L3充当内存,且必须处理每个核心私有的L1和L2缓存的一致性。正是这一观察促使一些设计者选择在多核系统中采用目录协议。为了使其有效,L3缓存必须是包含性的;回想一下第2章,一种缓存是包含性的,如果较高层级缓存(在此情况下为L1和L2)中的任何位置也在L3中。我们将在423页回到包含性主题。
图5.4展示了一个带有写回缓存的失效协议的示例。为了理解该协议如何确保一致性,可以考虑一个处理器的写操作后,另一个处理器的读操作:由于写入需要独占访问,任何由读取处理器持有的副本必须失效(因此得名)。因此,当读取发生时,它会在缓存中未命中,必须强制获取数据的新副本。对于写操作,我们要求写入处理器具有独占访问权限,防止任何其他处理器能够同时写入。如果两个处理器确实尝试同时写入相同的数据,其中一个会赢得竞争(我们稍后会看到如何决定谁胜出),导致另一个处理器的副本失效。为了完成写入,另一个处理器必须获取数据的新副本,而此副本现在必须包含更新的值。由此可见,该协议强制执行写入序列化。
与失效协议的替代方案是在写入数据项时更新所有缓存的副本。这种类型的协议被称为写入更新或写入广播协议。由于写入更新协议必须将所有写入广播到共享缓存行,因此它消耗的带宽显著更多。因此,几乎所有近期的多处理器都选择实现写入失效协议,我们将在本章剩余部分专注于失效协议。
### 基本实现技术
在多核系统中实施失效协议的关键是利用总线或其他广播媒介来执行失效操作。在较早的多芯片多处理器中,用于保持一致性的总线是共享内存访问总线。在单芯片多核中,总线可以是私有缓存(如Intel i7中的L1和L2)与共享外部缓存(如i7中的L3)之间的连接。要执行失效操作,处理器只需获得总线访问权限,并在总线上广播要失效的地址。所有处理器会持续监听总线,观察地址。如果总线上的地址在它们的缓存中存在,则相应的数据会被标记为无效。
当对共享块进行写操作时,写入处理器必须获得总线访问权限以广播其失效。在两个处理器尝试同时写入共享块时,它们在争用总线时将会序列化其失效操作。第一个获得总线访问权限的处理器会使它正在写入的块的其他副本失效。如果处理器尝试写入同一个块,总线强制的序列化也将序列化它们的写入。这种方案的一个含义是,对共享数据项的写入在获得总线访问之前实际上无法完成。所有一致性方案都需要某种方法来序列化对同一缓存块的访问,或者通过序列化对通信媒介的访问,或对其他共享结构的访问。
除了使正在写入的缓存块的未决副本失效外,我们还需要在发生缓存未命中的时候找到数据项。在直写缓存中,查找数据项的最新值很简单,因为所有写入的数据始终会发送到内存,从中可以始终获取数据项的最新值。(写缓冲区可能导致一些额外的复杂性,必须有效地视为额外的缓存条目。)
对于写回缓存,查找最新数据值的问题更为复杂,因为数据项的最新值可能在私有缓存中,而不是在共享缓存或内存中。幸运的是,写回缓存可以对缓存未命中和写入使用相同的监听机制:每个处理器监听所有放置在共享总线上的地址。如果某个处理器发现它有请求的缓存块的脏副本,它会在响应读取请求时提供该缓存块,并使内存(或L3)访问被中止。额外的复杂性来自于需要从另一个处理器的私有缓存(L1或L2)中检索缓存块,这通常比从L3中检索要花费更长的时间。由于写回缓存对内存带宽的要求较低,因此可以支持更多数量的更快处理器。因此,所有多核处理器在缓存的最外层级别上都使用写回缓存,我们将研究带有写回缓存的一致性实现。
正常的缓存标签可以用于实现监听过程,每个块的有效位使得失效操作的实现变得简单。读取未命中,无论是由于失效还是其他事件引起的,都很直接,因为它们仅依赖于监听能力。对于写入,我们希望知道该块是否有其他副本被缓存,因为如果没有其他缓存副本,则在写回缓存中写入不需要放置在总线上。不发送写入可以减少写入时间和所需带宽。
为了跟踪缓存块是否共享,我们可以为每个缓存块添加一个额外的状态位,就像我们有有效位和脏位一样。通过添加一个指示块是否共享的位,我们可以决定写入是否必须生成失效。当对处于共享状态的块进行写入时,缓存会在总线上生成失效并将该块标记为独占。此后,该核心不会再对该块发送进一步的失效请求。拥有缓存块唯一副本的核心通常被称为该缓存块的所有者。
当发送失效时,所有者的缓存块状态会从共享变为非共享(或独占)。如果另一个处理器随后请求这个缓存块,状态必须再次变为共享。由于我们的监听缓存也能看到任何未命中,它会知道独占缓存块何时被另一个处理器请求,从而将状态更改为共享。
每个总线事务必须检查缓存地址标签,这可能会干扰处理器的缓存访问。减少这种干扰的一种方法是复制标签,并将监听访问指向复制的标签。另一种方法是在共享的L3缓存中使用目录;该目录指示给定块是否共享,以及可能哪些核心有副本。通过目录信息,可以仅将失效请求定向到那些有缓存块副本的缓存。这要求L3始终必须有L1或L2中任何数据项的副本,这一特性称为包含性,我们将在第5.7节中回到这一点。
### 示例协议
窥探一致性协议通常通过在每个核心中集成一个有限状态控制器来实现。该控制器响应来自核心处理器和总线(或其他广播媒介)的请求,改变所选缓存块的状态,并利用总线访问数据或使其失效。逻辑上,可以将每个块视为有一个独立的控制器;也就是说,不同块的窥探操作或缓存请求可以独立进行。在实际实现中,单个控制器允许多个对不同块的操作以交错的方式进行(即,一个操作可以在另一个操作完成之前启动,尽管同一时间只能允许一次缓存访问或一次总线访问)。此外,请记住,尽管我们在以下描述中提到总线,但任何支持广播到所有一致性控制器及其相关私有缓存的互连网络都可以用于实现窥探。

图5.5 缓存一致性机制接收来自核心处理器和共享总线的请求,并根据请求的类型、在本地缓存中是否命中或缺失,以及请求中指定的本地缓存块的状态作出响应。第四列描述了缓存操作的类型,包括正常命中或缺失(与单处理器缓存相同)、替换(单处理器缓存替换缺失)或一致性(用于维持缓存一致性);正常或替换操作可能会引发一致性操作,具体取决于其他缓存中块的状态。对于从总线上窥探到的读取缺失、写入缺失或使失效的请求,仅当读取或写入地址与本地缓存中的某个块匹配且该块有效时,才需要采取相应的操作。
我们考虑的简单协议有三个状态:无效、共享和修改。共享状态表示私有缓存中的块可能是共享的,而修改状态表示该块在私有缓存中已被更新;需要注意的是,修改状态意味着该块是独占的。图5.5显示了核心生成的请求(表格的上半部分)以及来自总线的请求(表格的下半部分)。该协议适用于写回缓存,但通过将修改状态重新解释为独占状态,并按照写透缓存的正常方式在写入时更新缓存,可以轻松更改为写透缓存。该基本协议最常见的扩展是添加一个独占状态,描述一个未修改但仅保存在一个私有缓存中的块。我们将在第388页描述这个和其他扩展。
当无效化或写入缺失请求被放置在总线上时,任何拥有该缓存块副本的私有缓存的核心都会使其失效。对于写回缓存中的写入缺失,如果该块仅在一个私有缓存中是独占的,那么该缓存也会将该块写回;否则,数据可以从共享缓存或内存中读取。

图5.6 显示了一个用于私有写回缓存的写无效化缓存一致性协议,展示了缓存中每个块的状态和状态转换。缓存状态以圆圈表示,任何本地处理器在不发生状态转换的情况下允许的访问以括号形式显示在状态名称下。导致状态变化的刺激在转移弧线上用常规字体显示,而作为状态转换一部分生成的总线操作则用粗体显示。刺激行为适用于私有缓存中的一个块,而不是缓存中的特定地址。因此,对共享状态块的读取缺失对于该缓存块来说是缺失,但涉及的是不同的地址。图的左侧显示了基于与该缓存相关联的处理器操作的状态转换;右侧则显示基于总线操作的转换。当处理器请求的地址与本地缓存块中的地址不匹配时,在独占状态或共享状态下发生读取缺失和在独占状态下的写入缺失。这种缺失是标准的缓存替换缺失。尝试写入共享状态的块会产生无效化。每当发生总线事务时,所有包含总线事务中指定的缓存块的私有缓存都会采取图右侧规定的行动。该协议假设内存(或共享缓存)在读取缺失时提供干净块的数据信息。在实际实现中,这两组状态图是结合在一起的。在实践中,关于无效化协议存在许多微妙的变体,包括引入独占未修改状态,以及处理器或内存在缺失时提供数据的情况。在多核芯片中,共享缓存(通常是L3,有时是L2)相当于内存,而总线则是各核心私有缓存与共享缓存之间的总线,后者又与内存接口。
图5.6展示了使用写无效化协议和写回缓存的单个私有缓存块的有限状态转换图。为简化起见,协议的三个状态被复制,以表示基于处理器请求的转换(左侧,对应于图5.5上半部分的表格),与基于总线请求的转换(右侧,对应于图5.5下半部分的表格)相区分。加粗字体用于区分总线操作,而不是依赖于状态转换的条件。每个节点中的状态表示由处理器或总线请求指定的选定私有缓存块的状态。
在单处理器缓存中,所有这些状态都是必要的,它们对应于无效、有效(干净)和脏状态。图5.6左半部分中通过弧线表示的大多数状态变化在写回单处理器缓存中都是需要的,唯一的例外是对共享块的写命中的无效化。图5.6右半部分中通过弧线表示的状态变化仅用于一致性,并且在单处理器缓存控制器中不会出现。
如前所述,每个缓存只有一个有限状态机,其刺激来自附加的处理器或总线。图5.7展示了图5.6右半部分中的状态转换如何与左半部分中的状态转换结合,以形成每个缓存块的单一状态图。
为了理解这个协议为何有效,需要注意到任何有效的缓存块要么在一个或多个私有缓存中处于共享状态,要么在恰好一个缓存中处于独占状态。任何转变为独占状态(这对于处理器写入该块是必要的)都需要在总线上放置无效化或写缺失信号,导致所有本地缓存将该块标记为无效。此外,如果其他某个本地缓存中的块处于独占状态,该本地缓存会生成写回操作,从而提供包含所需地址的块。最后,如果在总线上对一个处于独占状态的块发生读取缺失,拥有独占副本的本地缓存会将其状态更改为共享。

图5.7中灰色部分的操作处理了总线上的读写缺失,这本质上是该协议的监听组件。该协议及大多数其他协议保持的另一个属性是,任何处于共享状态的内存块在外部共享缓存(L2或L3,或者如果没有共享缓存则是内存)中始终是最新的,这简化了实现。实际上,从私有缓存出来的层级是共享缓存还是内存并不重要;关键在于所有来自核心的访问都通过这一层级。
尽管我们的简单缓存协议是正确的,但它省略了一些使实现更加复杂的细节。其中最重要的是,该协议假设操作是原子的——即,操作可以以一种方式执行,使得没有其他干预操作可以发生。例如,所描述的协议假设可以检测到写缺失、获得总线并作为单一原子操作接收响应。实际上,这并不是事实。实际上,甚至读取缺失也可能不是原子的;在多核系统中,核心在L2中检测到缺失后,必须争夺连接到共享L3的总线访问权。非原子操作引入了协议可能死锁的可能性,这意味着它达到了无法继续的状态。我们将在本节稍后探讨这些复杂性,并在研究分布式共享内存设计时进一步讨论。
随着多核处理器的发展,处理器核心之间的缓存一致性都是在芯片上实现的,通常使用监听协议或简单的中央目录协议。许多多处理器芯片,包括英特尔的Xeon和AMD的Opteron,支持通过连接芯片中已有的高速接口构建的多芯片多处理器。这些更高级别的互连不仅仅是共享总线的扩展,而是采用不同的方法来互连多核处理器。由多个多核芯片构建的多处理器通常具有分布式内存架构,并需要一种在芯片之外的芯片间一致性机制。在大多数情况下,都会使用某种形式的目录方案。
### 基本一致性协议的扩展
我们刚刚描述的一致性协议是一个简单的三状态协议,通常用状态的首字母来称呼,因此称为MSI(修改、共享、无效)协议。这个基本协议有许多扩展,我们将在本节图形的说明中提到。这些扩展通过添加额外的状态和事务来优化某些行为,从而可能提高性能。最常见的两种扩展是:
1. **MESI**:在基本的MSI协议中添加了“独占”状态,形成四种状态(修改、独占、共享和无效)。独占状态表示缓存块仅存在于单个缓存中并且是干净的。如果某个块处于E状态,可以在不生成任何无效通知的情况下写入,这优化了在单个缓存中读取后再写入同一缓存的情况。当然,当对E状态的块发生读取未命中时,该块必须转换为S状态以维持一致性。由于所有后续访问都被监听,能够保持该状态的准确性。特别是,如果另一个处理器发出读取未命中请求,状态会从独占变为共享。添加此状态的优势在于,同一核心对独占状态的块后续写入无需获取总线访问权限或生成无效通知,因为该块已知仅在本地缓存中;处理器只需将状态更改为修改。这个状态可以通过将编码一致性状态的位用作独占状态,并使用脏位指示块已被修改来轻松添加。英特尔i7使用了一种MESI协议的变体,称为MESIF,增加了一个状态(转发),用于指定哪个共享处理器应响应请求。该设计旨在提高分布式内存组织中的性能。
2. **MOESI**:在MESI协议中添加了“拥有”状态,以指示相关块由该缓存拥有且主存中已过期。在MSI和MESI协议中,当尝试共享处于修改状态的块时,状态会变为共享(在原始缓存和新共享缓存中均如此),并且该块必须写回内存。在MOESI协议中,可以在原始缓存中将块从修改状态更改为拥有状态,而无需写入内存。其他新共享该块的缓存保持块处于共享状态;只有原始缓存持有的O状态表示主存中的副本已过期,并且指定的缓存是所有者。块的所有者在发生未命中时必须提供该块,因为内存未更新,并且如果被替换则必须将该块写回内存。AMD Opteron处理器系列使用MOESI协议。
下一部分将考察这些协议在我们的并行和多程序工作负载中的性能;当我们检查性能时,这些扩展对基本协议的价值将会显而易见。但在此之前,让我们简要看看对使用对称内存结构和监听一致性方案的限制。
### 对称共享内存多处理器和监听协议的局限性
随着多处理器中处理器数量的增加,或各处理器对内存的需求增长,系统中的任何集中资源都可能成为瓶颈。在多核系统中,即使只有少数核心,单一的共享总线也会成为瓶颈。因此,多核设计采用了更高带宽的互连方案,以及多个独立的内存,以支持更多核心。我们在第5.8节中审查的多核芯片使用了三种不同的方法:
1. **IBM Power8**:在单个多核中最多可以容纳12个处理器,使用8条并行总线连接分布式L3缓存和最多8个独立内存通道。
2. **Xeon E7**:使用三条环路连接最多32个处理器、一个分布式L3缓存以及两个或四个内存通道(具体取决于配置)。
3. **Fujitsu SPARC64 X+**:使用交叉开关将共享L2连接到最多16个核心和多个内存通道。
SPARC64 X+是一个对称组织,具有统一的访问时间。Power8则在L3和内存方面具有非统一的访问时间。尽管在单个Power8多核内,不同内存地址之间的无争用访问时间差异并不大,但在内存存在争用时,即便是在同一芯片内,这些访问时间差异可能变得显著。Xeon E7可以像访问时间统一一样运行;在实践中,软件系统通常会将内存组织为与部分核心相关联的内存通道。
缓存中的监听带宽也可能成为问题,因为每个缓存必须检查每一次未命中,而额外的互连带宽只能将问题转移到缓存。为了理解这个问题,可以考虑以下示例。
### 示例
考虑一个8处理器的多核系统,其中每个处理器都有自己的L1和L2缓存,并且在L2缓存之间通过共享总线进行监听。假设平均L2请求,无论是由于一致性未命中还是其他未命中,都是15个时钟周期。假设时钟频率为3.0 GHz,CPI(每指令周期)为0.7,加载/存储频率为40%。
如果我们的目标是确保一致性流量不超过L2带宽的50%,那么每个处理器的最大一致性未命中率是多少?
### 解答
首先,我们从可用缓存周期的方程开始(其中CMR为一致性未命中率):

这意味着一致性未命中率必须为0.73%或更低。在下一部分,我们将看到一些应用的一致性未命中率超过1%。另外,如果我们假设CMR可以达到1%,那么我们最多可以支持不到6个处理器。显然,即使是小型多核系统也需要一种方法来扩展监听带宽。
有几种技术可以增加监听带宽:
1. 正如前面提到的,标签可以进行重复。这将有效地将缓存级别的监听带宽翻倍。如果我们假设一半的一致性请求在监听请求上没有命中,并且监听请求的成本只有10个周期(与15个周期相比),那么我们可以将CMR的平均成本降低到12.5个周期。这一减少允许一致性未命中率达到0.88,或者支持一个额外的处理器(7个与6个相比)。
2. 如果多核系统中最外层的缓存(通常是L3)是共享的,我们可以将该缓存分配给每个处理器,使每个处理器拥有一部分内存并处理该地址空间部分的监听请求。这种方法被IBM 12核Power8采用,导致了一种NUCA设计,但有效地根据处理器数量扩展L3的监听带宽。如果L3中出现监听命中,我们仍然必须向所有L2缓存广播,这些缓存又必须监听其内容。由于L3充当了监听请求的过滤器,因此L3必须是包含性的。
3. 我们可以在最外层共享缓存(例如L3)上放置一个目录。L3充当监听请求的过滤器,并且必须是包含性的。在L3使用目录意味着我们不必对所有L2缓存进行监听或广播,而只需对目录指示可能拥有块副本的缓存进行操作。正如L3可以被分布,相关的目录条目也可以分布。这种方法在支持8到32个核心的Intel Xeon E7系列中得到了应用。

图5.8 显示了一个具有分布式缓存的单芯片多核处理器。在当前设计中,分布式共享缓存通常是L3,而L1和L2级别是私有的。通常有多个内存通道(在今天的设计中为2到8个)。该设计是NUCA,因为访问L3各部分的时间因直接连接的核心而异,访问时间更快。由于它是NUCA,因此它也是NUMA。
图5.8展示了一个具有分布式缓存系统的多核处理器的示例,类似于方案2或方案3中使用的系统。如果要添加更多的多核芯片以形成更大的多处理器系统,则需要一个外部网络以及扩展一致性机制的方法(我们将在第5.8节中讨论)。
AMD Opteron代表了一种介于监听协议和目录协议之间的中间点。内存直接连接到每个多核芯片,并且最多可以连接四个多核芯片。该系统是NUMA,因为本地内存的速度相对较快。Opteron通过点对点链接实现其一致性协议,以广播到多达三个其他芯片。由于处理器间的链接不共享,因此处理器知道无效操作何时完成的唯一方式是通过显式确认。因此,一致性协议使用广播来查找潜在共享的副本,类似于监听协议,但使用确认来排序操作,就像目录协议一样。由于在Opteron实现中,本地内存的速度仅比远程内存稍快,一些软件将Opteron多处理器视为具有统一内存访问。
在第5.4节中,我们讨论了基于目录的协议,这种协议消除了在缺失时向所有缓存广播的需求。一些多核设计在多核内部使用目录(例如Intel Xeon E7),而另一些则在规模超出多核时添加目录。分布式目录消除了串行化所有访问的单一节点需求(通常是监听方案中的单一共享总线),任何去除单一串行化节点的方案都必须面对与分布式目录方案类似的许多挑战。
### 实现窥探缓存一致性
魔鬼藏在细节中。
——经典谚语
当我们在1990年撰写本书的第一版时,我们最终的“整合”是一个使用基于窥探一致性的30处理器单总线多处理器系统;该总线的带宽刚好超过50 MiB/s,这已不足以支持2017年一颗Intel i7核心的带宽!在1995年撰写本书第二版时,首批具有多个总线的缓存一致性多处理器刚刚出现,我们在附录中增加了描述在多个总线系统中实现窥探的方法。到2017年,所有支持8个或更多核心的多核多处理器系统都使用除了单总线之外的互连,设计人员必须面对在没有简化总线来串行化事件的情况下实施窥探(或目录方案)的挑战。
正如我们在第386页所观察到的,实际实现我们所描述的窥探一致性协议的主要复杂性在于,写入和升级失效在最近的多处理器中并不是原子的。检测写入或升级失效、与其他处理器和内存通信、获取写入失效的最新值、确保任何失效请求被处理以及更新缓存的步骤不能视为在单个周期内完成。
在具有单一总线的多核系统中,这些步骤可以通过首先争用共享缓存或内存的总线(在更改缓存状态之前)并且在所有操作完成之前不释放总线,从而有效地使这些步骤变得原子。处理器如何知道所有失效请求何时完成?在早期设计中,使用单一信号线来指示所有必要的失效请求已经接收并正在处理。收到该信号后,产生失效的处理器可以释放总线,知道任何所需的操作将在与下一个失效相关的活动之前完成。通过在这些步骤中独占总线,处理器有效地使各个步骤具有了原子性。
在没有单一中央总线的系统中,我们必须找到其他方法来使失效步骤具有原子性。特别是,我们必须确保两个处理器在同一时间尝试写入同一块数据,这种情况称为竞争,能够严格排序:一个写入被处理并在下一个写入开始之前完成。两个写入在竞争中哪一个获胜并不重要,只要有一个赢家,其一致性操作首先完成。在使用多个总线的多核系统中,如果每个内存块只与单一总线关联,则可以消除竞争,这样对同一块的两个访问请求必须通过该公共总线串行化。这一特性,以及在竞争中重新启动失败者的失效处理能力,是在没有总线的情况下实现窥探缓存一致性的关键。我们在附录I中解释了细节。
结合窥探和目录也是可能的,多个设计在多核内部使用窥探,并在多个芯片之间使用目录,或者在一个缓存层级使用目录,在另一个层级使用窥探的组合。
5.3 Performance of Symmetric Shared-Memory Multiprocessors
在使用窥探一致性协议的多核系统中,多个不同现象共同决定性能。特别是,整体缓存性能是单处理器缓存未命中流量行为与由通信引起的流量(导致失效和随后的缓存未命中)之间的结合。改变处理器数量、缓存大小和块大小会以不同方式影响这两个未命中率的组成部分,从而导致整体系统行为是这两种效应的组合。
附录B将单处理器的未命中率分为三类(容量、强制和冲突),并提供了对应用行为和潜在缓存设计改进的洞察。同样,由于处理器之间的通信而产生的未命中,通常称为一致性未命中,可以分为两个独立的来源。
第一个来源是真共享未命中,这种未命中源于通过缓存一致性机制传递数据的通信。在基于失效的协议中,一个处理器对共享缓存块的第一次写入会导致失效,以确立该块的所有权。此外,当另一个处理器试图读取该缓存块中的修改字时,会发生未命中,并转移相应的块。这两种未命中都被归类为真共享未命中,因为它们直接源于处理器之间的数据共享。
第二种效应称为假共享,它源于使用每个缓存块一个有效位的基于失效的一致性算法。当一个块被失效(而后续引用导致未命中)是因为块中除被读取的字之外的其他字被写入时,就会发生假共享。如果写入的字实际上被接收到失效的处理器使用,那么该引用就是一个真共享引用,并且将导致未命中,而不依赖于块大小。然而,如果被写入的字和被读取的字不同,并且失效没有导致新值的传递,而只是导致额外的缓存未命中,那么这就是假共享未命中。在假共享未命中中,块是共享的,但缓存中的字实际上并没有共享,如果块大小为一个字,则不会发生未命中。以下示例清楚地说明了共享模式。
例子 假设字 z1 和 z2 位于同一个缓存块中,该块在 P1 和 P2 的缓存中处于共享状态。根据以下事件序列,判断每个未命中是一个真共享未命中、假共享未命中还是命中。任何如果块大小为一个字而发生的未命中被指定为真共享未命中。

回答 下面是按时间步骤分类的结果:
1. 该事件是一个真共享未命中,因为 z1 在 P2 中处于共享状态,需要从 P2 中失效。
2. 该事件是一个假共享未命中,因为 z2 被 P1 中对 z1 的写操作失效,但 P2 并没有使用 z1 的值。
3. 该事件是一个假共享未命中,因为包含 z1 的块由于 P2 的读取而标记为共享,但 P2 并没有读取 z1。在 P2 读取后,包含 z1 的缓存块将处于共享状态;需要一个写未命中以获得对该块的独占访问。在某些协议中,这将被视为升级请求,生成一个总线失效,但不会传输缓存块。
4. 该事件是一个假共享未命中,原因与第 3 步相同。
5. 该事件是一个真共享未命中,因为读取的值是由 P2 写入的。尽管我们将在商业工作负载中看到真共享和假共享未命中的影响,但对于共享大量用户数据的紧耦合应用程序,缓存一致性未命中的作用更为显著。当我们考虑并行科学工作负载的性能时,我们将在附录 I 中详细分析它们的影响。
### 商业工作负载
在本节中,我们将研究一个4处理器共享内存多处理器在运行在线事务处理工作负载时的内存系统行为。我们所研究的研究是在1998年使用一台4处理器Alpha系统进行的,但它仍然是对这种工作负载的多处理器性能最全面和深刻的研究。我们将重点理解多处理器缓存活动,特别是L3中的行为,因为这里的大部分流量与一致性相关。

图5.9 本研究中使用的Alpha 21164和Intel i7的缓存层次结构特征。尽管i7的缓存大小更大且关联度更高,但未命中惩罚也更高,因此两者的行为可能仅略有不同。这两个系统在从私有缓存进行的数据传输时都有较高的惩罚(125个周期或更多)。一个关键的区别是,i7的L3缓存是共享的,而Alpha服务器则有四个独立的、不共享的缓存。
结果是在AlphaServer 4100上收集的,或者使用基于AlphaServer 4100模型的可配置模拟器进行收集。AlphaServer 4100中的每个处理器都是Alpha 21164,每个时钟周期能够发出多达四条指令,运行频率为300 MHz。尽管该系统中Alpha处理器的时钟频率明显低于2017年设计的系统中的处理器,但系统的基本结构,包括四发射处理器和三级缓存层次结构,与多核Intel i7及其他处理器非常相似,如图5.9所示。我们不关注性能细节,而是考虑模拟L3行为的数据,该数据涉及每个处理器的L3缓存大小从2到8 MiB不等。
虽然原始研究考虑了三种不同的工作负载,但我们将重点关注以TPC-B为模型的在线事务处理(OLTP)工作负载(其内存行为类似于其较新的对应物TPC-C,如第一章所述),并使用Oracle 7.3.2作为底层数据库。该工作负载由一组生成请求的客户端进程和一组处理这些请求的服务器组成。服务器进程消耗了85%的用户时间,其余时间分配给客户端。尽管I/O延迟通过精心调整和足够的请求来隐藏,以保持处理器忙碌,但服务器进程通常在约25,000条指令后会因I/O而阻塞。总体而言,71%的执行时间是在用户模式下,18%在操作系统中,11%处于空闲状态,主要是等待I/O。
在所研究的商业应用中,OLTP应用对内存系统的压力最大,即使在评估更大L3缓存时也显示出显著的挑战。例如,在AlphaServer上,处理器大约有90%的周期处于停滞状态,内存访问占据了几乎一半的停滞时间,而L2未命中则占据了25%的停滞时间。
我们首先研究L3缓存大小变化的影响。在这些研究中,L3缓存的大小从每个处理器1 MiB变化到8 MiB;在每个处理器2 MiB时,L3的总大小与Intel i7 6700相等。然而,在i7的情况下,缓存是共享的,这带来了一些优缺点。共享的8 MiB缓存不太可能超过总大小为16 MiB的独立L3缓存。图5.10显示了使用二路组相联缓存增加缓存大小的影响,这减少了大量的冲突未命中。随着L3缓存的增大,执行时间得以改善,这是由于L3未命中的减少。令人惊讶的是,几乎所有的增益都是在从1 MiB增长到2 MiB(或四个处理器的总缓存从4 MiB增长到8 MiB)时实现的。尽管2 MiB和4 MiB缓存的未命中仍然导致显著的性能损失,但此后几乎没有额外的增益。问题是,为什么会这样?

图5.10 随着L3缓存大小从1 MiB增加到8 MiB(设置为二路组相联)的OLTP工作负载相对性能。随着缓存大小的增加,空闲时间也在增长,从而减少了一些性能提升。这种增长发生是因为内存系统的停滞减少,导致需要更多的服务器进程来覆盖I/O延迟。可以调整工作负载以提高计算/通信的平衡,从而控制空闲时间。PAL代码是一组在特权模式下执行的专用操作系统级指令序列;一个例子是TLB未命中处理程序。
为了更好地理解这个问题的答案,我们需要确定哪些因素影响L3未命中率,以及当L3缓存增大时这些因素如何变化。图5.11显示了这些数据,展示了来自五个来源的每条指令贡献的内存访问周期数。1 MiB L3缓存的两个最大内存访问周期来源是指令未命中和容量/冲突未命中。随着L3缓存的增大,这两个来源缩小为次要贡献者。不幸的是,强制未命中、虚假共享和真实共享未命中不会受到更大L3缓存的影响。因此,在4和8 MiB时,真实共享未命中占据了未命中的主要比例;真实共享未命中的变化缺乏导致在将L3缓存大小增加到超过2 MiB时整体未命中率的减少有限。

增加缓存大小消除了大部分单处理器未命中,同时对多处理器未命中影响不大。增加处理器数量如何影响不同类型的未命中呢?图5.12显示了这些数据,假设基准配置为2 MiB的二路组相联L3缓存(每个处理器的有效缓存大小与i7相同,但关联度较低)。正如我们所预期的,真实共享未命中率的增加并没有通过单处理器未命中的减少来进行补偿,导致每条指令的内存访问周期整体增加。

我们最后探讨的问题是,增加块大小是否对这个工作负载有帮助——这应该会降低指令未命中率和冷未命中率,并在一定范围内减少容量/冲突未命中率,可能还会降低真实共享未命中率。图5.13显示了随着块大小从32字节增加到256字节,每1000条指令的未命中次数。

增加块大小从32字节到256字节影响了四个未命中率组成部分:
- 真实共享未命中率减少了超过2倍,表明真实共享模式中存在一定的局部性。
- 强制未命中率显著下降,这符合我们的预期。
- 冲突/容量未命中率略有下降(与块大小增加8倍相比,仅减少1.26倍),表明在L3缓存大于2 MiB的单处理器未命中中,空间局部性并不高。
- 尽管绝对数值较小,虚假共享未命中率几乎翻倍。
指令未命中率没有显著变化令人惊讶。如果存在一个仅用于指令的缓存并表现出这种行为,我们可以得出结论,空间局部性非常差。在混合的L2和L3缓存情况下,指令-数据冲突等其他因素也可能导致更大块大小的高指令缓存未命中率。其他研究已记录了大型数据库和OLTP工作负载中指令流的低空间局部性,这些工作负载具有大量短基本块和专用代码序列。基于这些数据,使得更大块大小的L3性能与32字节块大小的L3相当的未命中惩罚,可以表示为32字节块大小惩罚的乘数。

随着现代DDR SDRAM使块访问变得快速,这些数字是可以实现的,特别是在64字节(i7块大小)和128字节块大小下。当然,我们还必须考虑增加内存流量带来的影响,以及可能与其他核心争用内存的情况。后者的影响可能很容易抵消通过提升单个处理器性能所获得的收益。
### 多道程序设计和操作系统工作负载
我们的下一个研究是一个多道程序工作负载,包含用户活动和操作系统活动。使用的工作负载是两个独立的安德鲁基准测试的编译阶段,该基准测试模拟软件开发环境。编译阶段由在八个处理器上执行的UNIX“make”命令的并行版本组成。该工作负载在八个处理器上运行5.24秒,创建了203个进程,并在三个不同的文件系统上执行了787个磁盘请求。工作负载使用128 MiB的内存,并且没有发生页面调度活动。
该工作负载有三个明显的阶段:编译基准,这涉及大量计算活动;将目标文件安装到库中;以及删除目标文件。最后一个阶段完全被I/O占主导地位,只有两个进程处于活跃状态(每个运行一个)。在中间阶段,I/O也发挥了主要作用,而处理器大部分时间处于空闲状态。总体工作负载比OLTP工作负载更具系统和I/O密集性。
对于工作负载的测量,我们假设以下内存和I/O系统:
- **一级指令缓存**——32 KB,双路组相联,块大小64字节,命中时间1个时钟周期。
- **一级数据缓存**——32 KB,双路组相联,块大小32字节,命中时间1个时钟周期。我们关注的是一级数据缓存的行为,与专注于L3缓存的OLTP研究形成对比。
- **二级缓存**——1 MiB统一,双路组相联,块大小128字节,命中时间10个时钟周期。
- **主内存**——单一内存在总线上,访问时间为100个时钟周期。
- **磁盘系统**——固定访问延迟为3毫秒(低于正常水平以减少空闲时间)。

图5.14 显示了多道程序并行“make”工作负载中执行时间的分布。高比例的空闲时间是由于当只有八个处理器中的一个处于活动状态时,磁盘延迟造成的。这些数据以及后续对该工作负载的测量是通过SimOS系统收集的(Rosenblum等,1995年)。实际的运行和数据收集由斯坦福大学的M. Rosenblum、S. Herrod和E. Bugnion完成。
图5.14展示了使用刚刚列出的参数时,八个处理器的执行时间分解。执行时间被分为四个组件:
1. 空闲——在内核模式空闲循环中的执行
2. 用户——在用户代码中的执行
3. 同步——执行或等待同步变量
4. 内核——在操作系统中执行的,既不是空闲也不是在同步访问中的部分
该多道程序工作负载在指令缓存性能上存在显著损失,至少对于操作系统而言。对于64字节块大小的双向组相联缓存,操作系统中的指令缓存缺失率从32 KB缓存的1.7%下降到256 KB缓存的0.2%。在各种缓存大小下,用户级指令缓存缺失率大约是操作系统缺失率的六分之一。这部分解释了尽管用户代码执行的指令数量是内核的九倍,但这些指令的执行时间仅约为内核执行的较少指令所需时间的四倍。
### 多道程序和操作系统工作负载的性能
在本节中,我们将分析多道程序工作负载在缓存大小和块大小变化时的缓存性能。由于内核和用户进程之间行为的差异,我们将这两部分分开讨论。但请记住,用户进程执行的指令数量超过八倍,因此总体缺失率主要由用户代码中的缺失率决定,而如我们所见,用户缺失率通常是内核缺失率的五分之一。
尽管用户代码执行更多的指令,但操作系统的行为可能会导致比用户进程更多的缓存缺失,这主要有两个原因,除了更大的代码大小和局部性缺失外。首先,内核在将所有页面分配给用户之前会先初始化它们,这显著增加了内核缺失率的强制组件。其次,内核实际上共享数据,因此具有不容忽视的一致性缺失率。相比之下,用户进程只有在被调度到不同处理器时才会导致一致性缺失,而这一缺失率的成分相对较小。这是多道程序工作负载与OLTP工作负载之间的一个主要区别。

图5.15显示了内核和用户组件在数据缺失率与数据缓存大小和块大小之间的关系。增加数据缓存大小对用户缺失率的影响大于对内核缺失率的影响。增加块大小对两者的缺失率都有积极的影响,因为较大比例的缺失来自强制性和容量缺失,而这两者都可以通过增大块大小来改善。由于一致性缺失相对较少,增加块大小的负面影响也较小。为了理解内核和用户进程的行为差异,我们可以观察内核缺失的表现。
图5.15 用户和内核组件的数据缺失率在L1数据缓存大小增加(左侧)与L1数据缓存块大小增加(右侧)时表现出不同的行为。当将L1数据缓存从32 KB增加到256 KB(使用32字节块)时,用户缺失率的下降幅度明显大于内核缺失率的下降幅度:用户级缺失率几乎下降了3倍,而内核级缺失率仅下降了1.3倍。在最大缓存大小时,L1的大小接近现代多核处理器中L2的大小。因此,这些数据表明,在L2缓存中,内核缺失率仍然会显著。随着L1块大小的增加(保持L1缓存为32 KB),用户和内核组件的缺失率均稳步下降。与增加缓存大小的影响相比,增加块大小对内核缺失率的改善更为显著(当块从16字节增加到128字节时,内核引用的缺失率下降接近4倍,而用户引用的缺失率下降不到3倍)。
图5.16显示了内核缺失率在缓存大小和块大小增加时的变化。缺失被分为三类:强制性缺失、一致性缺失(包括真实共享和虚假共享)以及容量/冲突缺失(其中包括操作系统与用户进程之间的干扰造成的缺失,以及多个用户进程之间的干扰)。图5.16确认,对于内核引用,增加缓存大小仅减少单处理器的容量/冲突缺失率。相比之下,增加块大小会减少强制性缺失率。随着块大小的增加,一致性缺失率没有大幅上升,这表明虚假共享效应可能并不显著,尽管这些缺失可能在一定程度上抵消了减少真实共享缺失带来的收益。

图5.16 当在八个处理器上运行多程序工作负载时,L1数据缓存大小从32 KB增加到256 KB时,内核数据缺失率的各个组成部分发生变化。强制缺失率组件保持不变,因为它不受缓存大小的影响。容量组件下降了超过2倍,而一致性组件几乎翻倍。一致性缺失的增加是因为随着缓存大小的增加,由无效化导致缺失的概率上升,因为由于容量原因被替换的条目更少。正如我们所预期的,L1数据缓存块大小的增加显著降低了内核引用中的强制缺失率。它对容量缺失率也产生了显著影响,使其在不同块大小范围内下降了2.4倍。增大的块大小对一致性流量有小幅减少,似乎在64字节时趋于稳定,且在使用128字节行时一致性缺失率没有变化。由于随着块大小的增加一致性缺失率没有显著下降,因此由一致性造成的缺失率比例从大约7%增长到大约15%。
如果我们查看每个数据引用所需的字节数,如图5.17所示,我们会发现内核的流量比率随着块大小的增加而增加。这种情况的原因很容易理解:当从16字节块切换到128字节块时,缺失率降低了约3.7倍,但每次缺失传输的字节数增加了8,因此总的缺失流量增加了略超过2倍。用户程序在块大小从16字节增加到128字节时也增加了超过2倍,但它起始时的水平要低得多。
对于多程序工作负载,操作系统对内存系统的需求要大得多。如果在工作负载中包含更多的操作系统或类似操作系统的活动,并且其行为与本工作负载的测量相似,那么构建一个足够强大的内存系统将变得非常困难。提升性能的一种可能途径是通过更好的编程环境或程序员的帮助,使操作系统更具缓存意识。例如,操作系统会重用来自不同系统调用的请求所占用的内存。尽管被重用的内存将被完全覆盖,但硬件由于未能识别这一点,会试图保持一致性,并假设某些缓存块的部分内容可能会被读取,即使实际上并不会。这种行为类似于在过程调用中重用栈位置。IBM Power 系列支持编译器在过程调用中指示这种行为,而最新的 AMD 处理器也有类似的支持。操作系统检测这种行为较为困难,可能需要程序员的协助,但潜在的收益可能更大。
操作系统和商业工作负载对多处理器内存系统提出了严峻的挑战,与我们在附录 I 中研究的科学应用不同,这些工作负载不太适合算法或编译器重构。随着核心数量的增加,预测此类应用程序的行为可能会变得更加困难。能够模拟数十到数百个核心的大型应用程序(包括操作系统)的仿真或模拟方法将对维持分析和定量设计方法至关重要。
5.4 Distributed Shared-Memory and Directory-Based Coherence
如我们在第5.2节中所见,监视协议在每次缓存未命中时都需要与所有缓存进行通信,包括对可能共享数据的写入。缺乏任何集中式数据结构来跟踪缓存状态既是基于监视方案的基本优势,因为这使其成本低廉,同时也是其在可扩展性方面的致命弱点。
例如,考虑一个由四个四核多核处理器组成的多处理器系统,这些处理器能够在4 GHz的时钟下每个时钟周期支持一个数据引用。从附录I的第I.5节中的数据来看,应用程序可能需要4到170 GiB/s的内存总线带宽。支持两个DDR4内存通道的i7的最大内存带宽为34 GiB/s。如果多个i7多核处理器共享同一内存系统,它们将很容易使其超负荷。在过去几年中,多核处理器的发展迫使所有设计者转向某种形式的分布式内存,以支持各个处理器的带宽需求。
我们可以通过分布内存来增加内存带宽和互连带宽,如第373页的图5.2所示;这立即将本地内存流量与远程内存流量分开,从而减少对内存系统和互连网络的带宽需求。除非我们消除一致性协议在每次缓存未命中时广播的需求,否则分布内存所带来的收益有限。
如我们之前提到的,监视协议的替代方案是目录协议。目录维护可能被缓存的每个块的状态。目录中的信息包括哪些缓存(或缓存集合)拥有该块的副本、该块是否为脏块等等。在具有共享最外层缓存(例如L3)的多核系统中,实现目录方案非常简单:只需为每个L3块保持一个与核心数量相等的位向量。该位向量指示哪些私有L2缓存可能拥有L3中的块副本,失效信息仅发送给这些缓存。如果L3是包含的,这对单个多核系统来说效果很好,而这种方案正是Intel i7所使用的。
在多核系统中使用单一目录的解决方案并不具备可扩展性,即使它避免了广播。目录必须是分布式的,但分布方式必须让一致性协议知道如何找到任何缓存的内存块的目录信息。显而易见的解决方案是将目录与内存一起分布,使得不同的一致性请求可以发送到不同的目录,就像不同的内存请求发送到不同的内存一样。如果信息保存在像L3这样的外部缓存中,并且是多银行的,则目录信息可以与不同的缓存银行一起分布,从而有效地增加带宽。
分布式目录保持了块的共享状态始终位于一个已知位置的特性。这个属性加上维护其他节点可能缓存该块的信息,使得一致性协议能够避免广播。图5.18展示了我们分布式内存多处理器的结构,目录被添加到每个节点。

图5.18 在每个节点添加了一个目录,以实现分布式内存多处理器中的缓存一致性。在这种情况下,节点被表示为单个多核芯片,相关内存的目录信息可以位于多核芯片内或外部。每个目录负责跟踪共享该节点内存地址的缓存。一致性机制将处理目录信息的维护以及多核节点内所需的任何一致性操作。
最简单的目录实现将目录中的条目与每个内存块关联。在这些实现中,信息量与内存块数量(每个块大小与L2或L3缓存块相同)与节点数量的乘积成正比,其中节点是单个多核处理器或小型处理器集合,这些处理器在内部实现一致性。对于少于几百个处理器的多处理器系统(每个处理器可能是多核),这种开销并不成问题,因为合理块大小下的目录开销是可以接受的。
对于更大的多处理器系统,我们需要方法来有效扩展目录结构,但只有超级计算机级别的系统需要担心这一点。
### 基于目录的缓存一致性协议:基础
与监听协议一样,目录协议必须实施两个主要操作:处理读取未命中和处理对共享、干净缓存块的写入(处理对当前共享块的写未命中是这两者的简单组合)。为了实现这些操作,目录必须跟踪每个缓存块的状态。在一个简单的协议中,这些状态可以是:
- **共享**——一个或多个节点缓存了该块,内存中的值是最新的(在所有缓存中也是如此)。
- **未缓存**——没有节点有该缓存块的副本。
- **修改**——恰好有一个节点有该缓存块的副本,并且已经写入该块,因此内存中的副本已过期。这个处理器被称为该块的所有者。
除了跟踪每个潜在共享内存块的状态外,我们还需要跟踪哪些节点有该块的副本,因为在写入时需要使这些副本失效。最简单的方法是为每个内存块保留一个位向量。当该块被共享时,位向量的每一位表示相应的处理器芯片(通常是多核)是否有该块的副本。当该块处于独占状态时,我们也可以利用位向量来跟踪该块的所有者。出于效率考虑,我们还在各个缓存中跟踪每个缓存块的状态。
每个缓存的状态机的状态和转换与我们在监听缓存中使用的相同,尽管转换时的操作略有不同。使无效和定位数据项的独占副本的过程不同,因为它们都涉及请求节点与目录之间以及目录与一个或多个远程节点之间的通信。在监听协议中,这两个步骤通过广播给所有节点的方式结合在一起。
在查看协议状态图之前,审视一系列可能在处理未命中和保持一致性时在处理器和目录之间发送的消息类型是有用的。图5.19展示了节点之间发送的消息类型。**本地节点**是请求发起的节点。**主节点**是存储内存位置和地址的目录条目的节点。物理地址空间是静态分布的,因此包含给定物理地址的内存和目录的节点是已知的。例如,高位可能提供节点编号,而低位提供该节点内存中的偏移量。本地节点也可能是主节点。当主节点是本地节点时,必须访问目录,因为副本可能存在于第三个节点,即远程节点。

图5.19 节点之间为维护一致性而发送的可能消息,包括源节点和目标节点、内容(其中 P5 为请求节点编号,A5 为请求地址,D5 为数据内容)以及消息的功能。前三条消息是本地节点向主节点发送的请求。第四到第六条消息是当主节点需要数据以满足读取或写入未命中请求时,发送给远程节点的消息。数据值回复用于将值从主节点发送回请求节点。数据值回写发生有两个原因:一是当缓存中的块被替换并且必须写回到其主存时,二是作为对来自主节点的获取或获取/失效消息的回复。每当块变为共享时写回数据值简化了协议中的状态数量,因为任何脏块必须是独占的,而任何共享块总是可以在主存中找到。
远程节点是指拥有缓存块副本的节点,无论是独占(在这种情况下,它是唯一的副本)还是共享。远程节点可以与本地节点或主节点相同。在这种情况下,基本协议不变,但处理器之间的消息可能会被处理器内部的消息所替代。
在本节中,我们假设一种简单的内存一致性模型。为了最小化消息类型和协议复杂性,我们假设消息将按照发送顺序接收和处理。这个假设在实际中可能并不成立,可能导致额外的复杂性,其中一些问题将在5.6节讨论内存一致性模型时得到解决。在本节中,我们使用这个假设来确保节点发送的失效消息在新消息传输之前得到响应,这与我们在实现监听协议时的假设一致。与监听案例一样,我们省略了一些实施一致性协议所需的细节。特别是,写操作的序列化以及确认写操作的失效已经完成并不像基于广播的监听机制那样简单。相反,需要对写未命中和失效请求进行明确的确认。我们将在附录I中更详细地讨论这些问题。
### 目录协议示例
在基于目录的协议中,缓存块的基本状态与监听协议中的状态完全相同,目录中的状态也类似于我们之前展示的。因此,我们可以首先从简单的状态图开始,显示单个缓存块的状态转换,然后检查与内存中每个块对应的目录项的状态图。与监听案例一样,这些状态转换图并不代表一致性协议的所有细节;然而,实际控制器高度依赖于多处理器的多个细节(消息传递特性、缓冲结构等)。在本节中,我们呈现基本的协议状态图。实现这些状态转换图所涉及的棘手问题将在附录I中讨论。
图5.20展示了单个缓存对协议操作的响应。我们使用与上一节相同的符号,来自节点外部的请求用灰色表示,操作用粗体字表示。单个缓存的状态转换是由读取未命中、写入未命中、失效和数据获取请求引起的;图5.20显示了这些操作。单个缓存还会生成发送到主目录的读取未命中、写入未命中和失效消息。读取和写入未命中需要数据值的回复,这些事件在改变状态之前会等待回复。知道失效何时完成是一个独立的问题,并将单独处理。

图5.20 显示了基于目录的系统中单个缓存块的状态转移图。来自本地处理器的请求用黑色表示,而来自主目录的请求用灰色表示。状态与监听案例中的状态相同,事务也非常相似,显式的失效和写回请求取代了之前在总线上广播的写未命中请求。与监听控制器相同,我们假设对共享缓存块的写入尝试被视为未命中;实际上,这种事务可以被视为所有权请求或升级请求,并且可以在不需要获取缓存块的情况下交付所有权。
图5.20中缓存块的状态转换图的操作本质上与监听案例相同:状态是相同的,刺激几乎也是相同的。在监听方案中,通过总线(或其他网络)广播的写入未命中操作被目录控制器选择性发送的数据获取和失效操作所替代。与监听协议一样,任何缓存块在被写入时必须处于独占状态,任何共享块在内存中必须是最新的。在许多多核处理器中,处理器缓存的最外层在各核心之间是共享的(如Intel i7、AMD Opteron和IBM Power7中的L3),而该级别的硬件维护着同一芯片上各核心私有缓存之间的一致性,使用内部目录或监听机制。因此,片上多核一致性机制可以通过接口连接到最外层共享缓存,将一致性扩展到更大集的处理器。由于此接口位于L3,因此处理器与一致性请求之间的争用问题较小,从而可以避免复制标签。
在基于目录的协议中,目录实现了一致性协议的另一半。发送到目录的消息会引发两种不同类型的操作:更新目录状态和发送额外消息以满足请求。目录中的状态表示缓存块的三种标准状态;然而,与监听方案不同,目录状态指示的是所有缓存副本的内存块状态,而不仅仅是单个缓存块。
内存块可能在任何节点上未缓存,也可能在多个节点上缓存且可读(共享),或者在一个节点上独占缓存且可写。除了每个块的状态外,目录还必须跟踪拥有该块副本的节点集合;我们使用一个称为“共享者”(Sharers)的集合来执行此功能。在节点少于64个的多处理器系统中(每个节点可能代表四到八个处理器),该集合通常以位向量的形式存储。目录请求需要更新共享者集合,并且还需读取该集合以执行失效操作。

图5.21 目录的状态转换图与单个缓存的转换图具有相同的状态和结构。所有操作均以灰色显示,因为它们都是由外部引起的。粗体表示目录对请求所采取的行动。
图5.21 显示了目录在收到消息后的操作。目录接收到三种不同的请求:读未命中、写未命中和数据写回。目录响应发送的消息以粗体显示,更新共享者集合的操作以粗斜体显示。由于所有刺激消息都是外部的,因此所有操作都用灰色表示。我们简化的协议假设某些操作是原子的,例如请求一个值并将其发送到另一个节点;而现实中的实现无法使用这一假设。
为了理解这些目录操作,我们逐状态检查所收到的请求和采取的行动。当一个块处于未缓存状态时,内存中的副本就是当前值,因此该块唯一可能的请求为:
- **读未命中**——请求节点从内存中获取请求的数据,并且请求者成为唯一的共享节点。该块的状态变为共享。
- **写未命中**——请求节点获得该值并成为共享节点。该块状态变为独占,以表明唯一有效副本已被缓存。共享者指示所有者的身份。
当块处于共享状态时,内存值是最新的,因此可以发生相同的两种请求:
- **读未命中**——请求节点从内存中获取请求的数据,并且请求节点被添加到共享集合中。
- **写未命中**——请求节点获取该值。所有在共享者集合中的节点都会收到失效消息,并且共享者集合将包含请求节点的身份。该块的状态变为独占。
当块处于独占状态时,块的当前值保存在由共享者集合(所有者)标识的节点的缓存中,因此有三种可能的目录请求:
- **读未命中**——向所有者发送数据获取消息,这会导致所有者缓存中的块状态转换为共享,并使所有者将数据发送到目录,在那里写入内存并返回给请求的处理器。请求节点的身份被添加到共享者集合中,该集合仍包含原所有者的身份(因为它仍然有一个可读副本)。
- **数据写回**——所有者正在替换该块,因此必须将其写回。这次写回使内存副本保持最新(主目录本质上成为所有者),该块现在变为未缓存状态,且共享者集合为空。
- **写未命中**——块有了新的所有者。向旧所有者发送消息,导致缓存失效该块并将值发送到目录,然后再发送到请求节点,该节点成为新所有者。共享者集合被设置为新所有者的身份,块的状态保持为独占。
图5.21中的状态转换图是一种简化,就像在监听缓存情况下那样。在目录和使用非总线网络实现的监听方案的情况下,我们的协议需要处理非原子内存事务。附录I深入探讨了这些问题。
在实际多处理器中使用的目录协议包含额外的优化。特别是在此协议中,当独占块发生读或写未命中时,块首先被发送到主节点的目录。从那里,它被存储到主内存并发送到最初请求的节点。
许多在商业多处理器中使用的协议直接将数据从所有者节点转发到请求节点(同时执行写回到主内存)。这样的优化通常通过增加死锁的可能性和需要处理的消息类型来增加复杂性。
实现目录方案需要解决与我们讨论的监听协议大部分相同的挑战。然而,还有一些新的附加问题,我们在附录I中进行了描述。在第5.8节中,我们简要介绍了现代多核如何将一致性扩展到单个芯片之外。多芯片一致性和多核一致性的组合包括所有四种可能性:监听/监听(AMD Opteron)、监听/目录、目录/监听以及目录/目录!许多多处理器选择在单个芯片内采用某种形式的监听,这在外层缓存共享且包含时特别有吸引力,而跨多个芯片则使用目录。这种方法简化了实现,因为只需跟踪处理器芯片,而不是单个核心。
5.5 Synchronization: The Basics
同步机制通常是通过依赖于硬件提供的同步指令的用户级软件例程构建的。对于较小的多处理器或低争用情况,关键的硬件能力是不可中断的指令或指令序列,能够原子性地检索和修改一个值。然后,软件同步机制基于这一能力构建。在本节中,我们重点讨论锁定和解锁同步操作的实现。锁定和解锁可以直接用于创建互斥,以及实现更复杂的同步机制。
在高争用情况下,同步可能成为性能瓶颈,因为争用会引入额外的延迟,并且在这样的多处理器中,延迟可能更大。我们在附录I中讨论了如何将本节的基本同步机制扩展到大处理器数量的情况。
### 基本硬件原语
我们在多处理器中实现同步所需的关键能力是一组硬件原语,能够原子性地读取和修改内存位置。如果没有这样的能力,构建基本同步原语的成本将过高,并且随着处理器数量的增加而增加。有多种基本硬件原语的替代形式,它们都提供原子性读取和修改位置的能力,并且有某种方法来判断读取和写入是否是原子执行的。这些硬件原语是构建各种用户级同步操作的基本构件,包括锁和屏障等内容。一般来说,架构师并不期望用户直接使用基本硬件原语,而是希望系统程序员利用这些原语构建同步库,这个过程往往复杂且棘手。
让我们从一种这样的硬件原语开始,展示它如何用于构建一些基本的同步操作。
一种构建同步操作的典型操作是原子交换,它将寄存器中的值与内存中的值互换。为了说明如何使用这一方法构建基本的同步操作,假设我们想构建一个简单的锁,其中值0表示锁是空闲的,而值1表示锁不可用。处理器通过将寄存器中的值1与对应于锁的内存地址进行交换来尝试设置锁。从交换指令返回的值是1(如果其他处理器已经声称访问权限)或0。在后者的情况下,值也会被改为1,从而防止任何竞争的交换再次获取到0。例如,考虑两个处理器同时尝试进行交换:这个竞争会被打破,因为恰好有一个处理器会先执行交换,返回0,而第二个处理器在执行交换时将返回1。使用交换(或置换)原语实现同步的关键在于操作是原子的:交换是不可分割的,并且两个同时进行的交换会被写入序列化机制所排序。因此,两个处理器不能同时确认它们已设置同步变量。
还有其他一些原子原语可以用于实现同步。它们都有一个关键特性,即以某种方式读取和更新内存值,使我们能够判断这两个操作是否以原子方式执行。一种在许多旧多处理器中存在的操作是测试并设置,它先测试一个值,如果值通过测试,则设置该值。例如,我们可以定义一个操作,测试值是否为0并将其设置为1,这可以像使用原子交换一样使用。另一个原子同步原语是取出并递增:它返回一个内存位置的值并原子性地递增该值。通过使用值0来表示同步变量未被占用,我们可以像使用交换一样使用取出并递增。还有其他使用取出并递增等操作的方法,我们将很快看到。
实现单个原子内存操作会带来一些挑战,因为这要求在一个不可中断的指令中同时进行内存读取和写入。这一要求使得一致性实现变得复杂,因为硬件不能在读取和写入之间允许任何其他操作,同时又必须避免死锁。
另一种选择是使用一对指令,其中第二条指令返回一个值,通过该值可以推断出这一对指令是否以原子方式执行。如果看起来所有其他由任何处理器执行的操作都发生在这一对指令之前或之后,那么这一对指令就是有效原子的。因此,当一对指令有效原子时,没有其他处理器可以在这一对指令之间更改值。这正是MIPS处理器和RISC-V采用的方法。
在 RISC-V 中,这对指令包括一个特殊的加载指令,称为保留加载(也称为链接加载或锁定加载),以及一个特殊的存储指令,称为条件存储。保留加载将由 rs1 指定的内存内容加载到 rd 中,并在该内存地址上创建一个保留。条件存储将 rs2 中的值存储到由 rs1 指定的内存地址。如果加载的保留因对同一内存位置的写入而被打破,则条件存储失败,并将非零值写入 rd;如果成功,则条件存储写入 0。如果处理器在这两条指令之间进行上下文切换,则条件存储总是失败。这些指令是按顺序使用的,因为保留加载返回初始值,而条件存储只有在成功时才返回 0,因此以下序列在由 x1 的内容指定的内存位置与 x4 中的值之间实现了原子交换:

在这个序列结束时,x4 的内容与由 x1 指定的内存位置已经被原子性地交换。每当处理器介入并在 lr 和 sc 指令之间修改内存中的值时,sc 会在 x3 中返回 0,从而导致代码序列重试。
保留加载/条件存储机制的一个优点是它可以用来构建其他同步原语。例如,下面是一个原子取出并递增的操作:

这些指令通常通过在一个寄存器中跟踪 lr 指令指定的地址来实现,这个寄存器通常称为保留寄存器。如果发生中断,或者链接寄存器中匹配地址的缓存块被失效(例如,由另一个 sc 指令),则链接寄存器会被清除。sc 指令简单地检查其地址是否与保留寄存器中的地址匹配。如果匹配,sc 就成功;否则,就失败。由于条件存储在对加载保留地址的另一次尝试存储或任何异常后都会失败,因此在选择插入这两条指令之间的指令时必须小心。特别是,只有寄存器到寄存器的指令可以安全地被允许;否则,可能会导致死锁情况,使处理器无法完成 sc。此外,在保留加载和条件存储之间的指令数量应尽量减少,以降低无关事件或竞争处理器频繁导致条件存储失败的概率。
### 使用一致性实现锁
一旦我们有了原子操作,就可以利用多处理器的缓存一致性机制来实现自旋锁——一种处理器持续尝试获取的锁,直到成功为止。自旋锁在程序员预期锁会被持有很短时间,并希望在锁可用时获取锁的延迟尽可能低的情况下使用。由于自旋锁会占用处理器在循环中等待锁释放,因此在某些情况下它们是不合适的。
最简单的实现(如果没有缓存一致性)是将锁变量保存在内存中。处理器可以不断尝试使用原子操作(例如,原子交换)来获取锁,并测试交换是否返回锁为空。要释放锁,处理器只需将值 0 存储到锁中。下面是锁定自旋锁的代码序列,其地址在 x1 中。它使用 EXCH 作为第 414 页的原子交换序列的宏:

如果我们的多处理器支持缓存一致性机制,我们可以利用这一机制对锁进行缓存,以保持锁值的一致性。缓存锁有两个优点。首先,它允许在“自旋”过程(尝试在紧密循环中测试并获取锁)中使用本地缓存副本,而不是在每次尝试获取锁时都进行全局内存访问。第二个优点源于对锁访问的局部性观察;也就是说,最后使用锁的处理器在不久的将来会再次使用它。在这种情况下,锁值可能存储在该处理器的缓存中,从而大大减少获取锁的时间。
获得第一个优点——能够在本地缓存副本上自旋,而不是在每次尝试获取锁时生成内存请求——需要对我们简单的自旋过程进行更改。在前面的循环中,每次尝试交换都需要写操作。如果多个处理器尝试获取锁,每个处理器都会生成写操作。其中大多数写操作将导致写失效,因为每个处理器都在尝试以独占状态获取锁变量。因此,我们应该修改自旋锁程序,使其通过在本地复制的锁上进行读取自旋,直到成功看到锁可用为止。然后,它尝试通过执行交换操作来获取锁。
处理器首先读取锁变量以测试其状态。处理器会持续读取和测试,直到读取值表明锁是解锁的。然后,处理器与所有其他同样在“自旋等待”的进程竞争,看看哪个能首先锁定变量。所有进程都使用一个交换指令,该指令读取旧值并将 1 存储到锁变量中。唯一的赢家会看到 0,而失败者看到的是赢家放置的 1。(失败者将继续将变量设置为锁定值,但这并不重要。)获胜的处理器在锁后执行代码,完成后将 0 存储到锁变量中以释放锁,从而重新开始竞争。以下是执行此自旋锁的代码(请记住,0 表示解锁,1 表示锁定):

让我们来看看这个“自旋锁”方案是如何利用缓存一致性机制的。图5.22展示了多个进程使用原子交换尝试锁定变量时的处理器和总线或目录操作。一旦持有锁的处理器将0存入锁中,所有其他缓存都会被失效,必须获取新值以更新它们的锁副本。其中一个缓存首先获取到解锁值(0)的副本并执行交换。当其他处理器的缓存未命中被满足时,它们发现变量已经被锁定,因此必须返回继续测试和自旋。

图5.22 展示了三个处理器 P0、P1 和 P2 的缓存一致性步骤和总线流量。该图假设采用写失效一致性。P0 在步骤 1 开始时持有锁,锁的值为 1(即,锁定状态);在步骤 1 开始之前,它最初是独占的,并由 P0 拥有。P0 退出并解锁(步骤 2)。P1 和 P2 竞争,看看哪个能在交换过程中读取到解锁值(步骤 3-5)。P2 胜出并进入临界区(步骤 6 和 7),而 P1 的尝试失败,因此它开始自旋等待(步骤 7 和 8)。在实际系统中,这些事件将花费远超过 8 个时钟周期,因为获取总线和响应未命中的时间要长得多。一旦达到步骤 8,过程可以与 P2 重复,最终获得独占访问权并将锁设置为 0。
这个例子展示了加载保留/条件存储原语的另一个优点:读写操作被明确分开。加载保留操作不需要引起任何总线流量。这一事实允许如下简单的代码序列,它具有与使用交换的优化版本相同的特征(x1是锁的地址,lr替代了LD,sc替代了EXCH):

第一个分支形成自旋循环;第二个分支在两个处理器同时看到锁可用时解决竞争条件。
5.6 Models of Memory Consistency: An Introduction
缓存一致性确保多个处理器看到内存的一致视图。它并没有回答内存视图必须有多一致的问题。我们所说的“多一致”实际上是在询问,一个处理器何时必须看到另一个处理器更新的值。由于处理器通过共享变量进行通信(这些变量既用于数据值也用于同步),问题归结为:处理器必须以什么顺序观察另一个处理器的数据写入?由于“观察另一个处理器的写入”的唯一方式是通过读取,因此问题变成了不同处理器在对不同位置进行读写时,必须强制执行哪些属性?
尽管内存必须保持多一致的问题看似简单,但实际上非常复杂,这一点可以通过一个简单的例子来说明。以下是来自 P1 和 P2 进程的两个代码段,呈并排显示:

假设这些进程在不同的处理器上运行,并且位置 A 和 B 最初都被两个处理器缓存,初始值为 0。如果写入操作总是立即生效并被其他处理器看到,那么两个 IF 语句(标记为 L1 和 L2)都无法同时评估为真,因为达到 IF 语句意味着 A 或 B 必须被赋值为 1。但假设写入失效被延迟,并且处理器在此延迟期间被允许继续执行。那么 P1 和 P2 可能在尝试读取值之前,都没有看到对 B 和 A 的失效。现在的问题是,这种行为是否应该被允许,如果允许,应该在什么条件下?
内存一致性的最简单模型称为顺序一致性。顺序一致性要求任何执行的结果都与每个处理器的内存访问按顺序执行,并且不同处理器之间的访问可以任意交错的情况相同。顺序一致性消除了在前一个示例中某些不明显执行的可能性,因为赋值必须在启动 IF 语句之前完成。
实现顺序一致性的最简单方法是要求处理器在完成所有因该内存访问而导致的失效之前,延迟任何内存访问的完成。当然,延迟下一个内存访问直到上一个访问完成也是同样有效的。记住,内存一致性涉及不同变量之间的操作:必须排序的两个访问实际上是指向不同的内存位置。在我们的例子中,我们必须延迟对 A 或 B 的读取(A == 0 或 B == 0),直到之前的写入完成(B=1 或 A=1)。在顺序一致性下,我们不能仅仅将写入放入写缓冲区然后继续读取。
尽管顺序一致性提供了一个简单的编程范式,但它降低了潜在的性能,特别是在具有大量处理器或长互连延迟的多处理器系统中,如以下示例所示。
**示例** 假设我们有一个处理器,其中写入失效需要 50 个周期来建立所有权,在所有权建立后,每个失效发出需要 10 个周期,并且一旦发出,失效完成并被确认需要 80 个周期。假设还有四个其他处理器共享一个缓存块,如果处理器是顺序一致的,写入失效会让写入处理器停滞多长时间?假设在获得写入失效的所有权后,我们可以继续执行而不必等待失效;那么写入将花费多长时间?
**回答** 当我们等待失效时,每次写入的总时间是所有权时间加上完成失效的时间。由于失效可以重叠,我们只需关注最后一个失效,它在所有权建立后 10 + 10 + 10 + 10 = 40 周期开始。因此,写入的总时间为 50 + 40 + 80 = 170 周期。相比之下,所有权时间仅为 50 个周期。通过适当的写缓冲区实现,甚至可以在所有权建立之前继续执行。
为了提供更好的性能,研究人员和架构师探索了两条不同的路线。首先,他们开发了雄心勃勃的实现,这些实现保持了顺序一致性,但使用延迟隐藏技术来减少惩罚;我们将在第 5.7 节讨论这些技术。其次,他们开发了不太严格的内存一致性模型,以允许更快的硬件。这些模型可能会影响程序员对多处理器的看法,因此在讨论这些不太严格的模型之前,让我们看看程序员的期望是什么。
### 程序员的视角
尽管顺序一致性模型在性能上存在劣势,但从程序员的角度来看,它具有简单性的优势。挑战在于开发一种简单易懂的编程模型,同时又能实现高性能的实现。
一种允许我们实现更高效实现的编程模型是假设程序是同步的。如果所有对共享数据的访问都通过同步操作进行排序,则该程序被认为是同步的。当一个处理器对变量进行写入,另一个处理器对该变量进行访问(无论是读取还是写入)时,如果在每种可能的执行中,这两个操作之间由一对同步操作分隔,则称该数据引用是有序的。这对同步操作包括一个在写入之后由写入处理器执行的操作,和一个在第二个处理器访问之前执行的操作。不按照同步排序可能更新变量的情况被称为数据竞争,因为执行结果取决于处理器的相对速度,正如硬件设计中的竞赛,结果是不可预测的,这也导致了同步程序的另一种称呼:无数据竞争。
作为一个简单的例子,考虑一个变量被两个不同处理器读取和更新。每个处理器在读取和更新操作前后都使用锁和解锁,以确保更新的互斥性,并确保读取的一致性。显然,每次写入现在都由一对同步操作分隔开:一个解锁(在写入之后)和一个锁(在读取之前)。当然,如果两个处理器在没有插入读取的情况下同时写入一个变量,那么这些写入也必须由同步操作分隔开。
普遍接受的观察是,大多数程序都是同步的。这个观察主要是因为,如果访问是无序的,程序的行为可能会变得不可预测,因为执行速度将决定哪个处理器赢得数据竞争,从而影响程序的结果。即便是在顺序一致性下,推理这样的程序也是非常困难的。
程序员可以尝试通过构造自己的同步机制来保证排序,但这非常棘手,可能导致程序出现错误,并且可能在架构上不受支持,意味着它们可能无法在多处理器的未来版本中工作。因此,几乎所有程序员都会选择使用针对多处理器和特定同步类型进行了优化的正确的同步库。
最后,使用标准的同步原语确保即使架构实现的比顺序一致性更宽松的一致性模型,同步程序的行为也将如同硬件实现了顺序一致性一样。
### 放松一致性模型:基础与释放一致性
放松一致性模型的关键思想是允许读写操作以无序方式完成,但通过同步操作来强制执行顺序,使得同步程序的行为看起来就像处理器是顺序一致的一样。这些放松模型根据它们放松的读取和写入顺序进行分类。我们通过一组规则来指定顺序,形式为 X!Y,表示操作 X 必须在操作 Y 完成之前完成。顺序一致性要求维护所有四种可能的顺序:R!W、R!R、W!R 和 W!W。放松模型则由它们放松的四个顺序的子集定义:
1. 仅放松 W!R 顺序会产生一个称为总存储顺序或处理器一致性的模型。由于该模型保留了写入之间的顺序,许多在顺序一致性下运行的程序在这个模型下也能正常工作,而无需额外的同步。
2. 同时放松 W!R 顺序和 W!W 顺序会产生一个称为部分存储顺序的模型。
3. 放松所有四种顺序会产生包括弱顺序、PowerPC 一致性模型和释放一致性(RISC V 一致性模型)在内的多种模型。
通过放松这些顺序,处理器可以获得显著的性能优势,这也是 RISC V、ARMv8 以及 C++ 和 C 语言标准选择释放一致性作为模型的原因。
释放一致性区分用于获取对共享变量访问的同步操作(称为 SA)和用于释放对象以允许其他处理器获取访问权限的操作(称为 SR)。释放一致性基于这样的观察:在同步程序中,获取操作必须在使用共享数据之前,而释放操作必须在对共享数据的任何更新之后,并且还必须在下一次获取之前。这一属性使我们能够稍微放宽顺序,观察到在获取之前的读取或写入不必在获取之前完成,以及在释放之后的读取或写入不必等待释放。因此,保留下来的顺序仅涉及 SA 和 SR,如图 5.23 所示;如图 5.24 中的示例所示,该模型施加了五种模型中最少的顺序。

图 5.23 各种一致性模型对普通访问和同步访问施加的顺序关系如图所示。这些模型从最严格的(顺序一致性)到最宽松的(释放一致性)逐渐放宽,允许实现上的更大灵活性。较弱的模型依赖于由同步操作创建的屏障,而不是在每个内存操作中隐含的屏障。SA 和 SR 分别代表获取和释放操作,是定义释放一致性所必需的。如果我们在每个 S 中一致地使用 SA 和 SR 的符号,则每个带有一个 S 的顺序将变成两个顺序(例如,S!W 变为 SA!W 和 SR!W),而每个 S!S 将变为右下表格最后一行中显示的四个顺序。

图 5.24 本节讨论的五种一致性模型的示例展示了随着模型放松,施加的顺序数量的减少。仅显示了带箭头的最小顺序。由于传递性隐含的顺序,例如在顺序一致性模型中 C 的写入在 S 的释放之前,或在弱顺序或释放一致性中获取操作在释放之前的顺序,并未显示。
释放一致性提供了一种限制最少的模型,易于检查,并确保同步程序将看到顺序一致的执行。尽管大多数同步操作要么是获取操作,要么是释放操作(获取操作通常读取一个同步变量并原子性地更新它,而释放操作通常只是写入它),某些操作,例如屏障,既充当获取又充当释放,从而使顺序等价于弱顺序。虽然同步操作始终确保先前的写入已完成,但我们可能希望保证写入在没有明确同步操作的情况下完成。在这种情况下,使用一种称为 FENCE 的显式指令来确保该线程中的所有先前指令都已完成,包括所有内存写入和相关失效的完成。有关放松模型的复杂性、实现问题和性能潜力的更多信息,我们强烈推荐 Adve 和 Gharachorloo(1996)的优秀教程。
5.7 Cross-Cutting Issues
由于多处理器重新定义了许多系统特性(例如,性能评估、内存延迟和可扩展性的重要性),它们引入了有趣的设计问题,涉及硬件和软件的各个方面。在本节中,我们给出几个与内存一致性问题相关的示例。随后,我们将探讨在多线程添加到多处理时所获得的性能提升。
#### 编译器优化与一致性模型
定义内存一致性模型的另一个原因是指定对共享数据可以执行的合法编译器优化范围。在显式并行程序中,除非同步点明确且程序已同步,否则编译器不能互换两个不同共享数据项的读写,因为这样的转换可能会影响程序的语义。这一限制甚至阻止了相对简单的优化,例如共享数据的寄存器分配,因为这一过程通常涉及读写的互换。在隐式并行化程序中,例如使用高性能福特兰(HPF)编写的程序,程序必须是同步的,并且同步点是已知的,因此不会出现这个问题。编译器是否能够从更宽松的一致性模型中获得显著优势仍然是一个悬而未决的问题,既从研究角度看,也从实际应用角度看,缺乏统一模型可能会阻碍编译器的部署进展。
### 使用推测来隐藏严格一致性模型中的延迟
正如我们在第三章中所看到的,推测可以用来隐藏内存延迟。它也可以用来隐藏由于严格一致性模型而产生的延迟,从而带来许多放宽内存模型的好处。关键思想是处理器使用动态调度重新排序内存引用,从而让它们可能以无序的方式执行。以无序执行内存引用可能会导致顺序一致性的违规,这可能会影响程序的执行。通过利用推测处理器的延迟提交特性,可以避免这种可能性。假设一致性协议基于失效机制。如果处理器在内存引用提交之前接收到对该内存引用的失效,处理器将使用推测恢复机制撤销计算,并从被失效地址的内存引用重新开始。
如果处理器对内存请求的重新排序导致的执行顺序可能产生与顺序一致性下不同的结果,处理器将重新执行这一过程。使用这种方法的关键在于,处理器只需保证结果与所有访问按顺序完成时的结果相同,而它可以通过检测结果何时可能有所不同来实现。这种方法具有吸引力,因为推测重启几乎不会被触发。只有在存在未同步访问并且实际导致竞争条件时,才会触发重启(Gharachorloo et al., 1992)。
Hill(1998)提倡将顺序一致性或处理器一致性与推测执行相结合,作为首选的一致性模型。他的论点分为三部分。首先,顺序一致性或处理器一致性的激进实现将获得更放宽模型的大部分优势。其次,这种实现对推测处理器的实现成本增加非常有限。第三,这种方法允许程序员使用更简单的顺序或处理器一致性的编程模型进行推理。MIPS R10000设计团队在1990年代中期意识到了这一点,并利用R10000的乱序能力支持这种激进的顺序一致性实现。
一个悬而未决的问题是编译器技术在优化对共享变量的内存引用方面会有多成功。优化技术的现状以及共享数据通常通过指针或数组索引访问的事实限制了此类优化的使用。如果这种技术能够得到应用并带来显著的性能优势,编译器开发者将希望能够利用更放宽的编程模型。这个可能性以及对未来保持灵活性的渴望促使RISC V设计者在经过一系列长时间的辩论后选择了发布一致性。
### 包含性及其实现
所有多处理器系统都使用多级缓存层次结构,以减少对全局互连的需求以及缓存未命中的延迟。如果缓存还提供多级包含性——每一级缓存层次都是比其更远离处理器的层级的子集——那么我们可以利用这种多级结构来减少在监视和处理器缓存访问必须争用缓存时所产生的一致性流量与处理器流量之间的竞争。许多具有多级缓存的多处理器系统强制执行包含性属性,尽管最近一些具有较小L1缓存和不同块大小的多处理器有时选择不强制执行包含性。这种限制也称为子集属性,因为每个缓存都是其下方缓存的子集。
乍一看,保持多级包含性属性似乎是微不足道的。考虑一个两级示例:在L1中的任何未命中要么在L2中命中,要么在L2中生成未命中,从而导致数据被引入到L1和L2中。同样,在L2中命中的任何失效必须发送到L1,如果该块存在,则会导致其被失效。然而,当L1和L2的块大小不同时,会发生什么呢?
选择不同的块大小是相当合理的,因为L2将会大得多,并且在其未命中惩罚中具有更长的延迟成分,因此希望使用更大的块大小。当块大小不同的时候,我们的“自动”包含性强制执行会发生什么?L2中的一个块代表多个L1中的块,而L2中的未命中会导致替换相当于多个L1块的数据。例如,如果L2的块大小是L1的四倍,那么L2中的未命中将替换相当于四个L1块的数据。让我们考虑一个详细的例子。
**示例**
假设L2的块大小是L1的四倍。展示一个地址的未命中如何导致L1和L2中的替换,从而违反包含性属性。
**回答**
假设L1和L2都是直接映射缓存,L1的块大小为b字节,而L2的块大小为4b字节。假设L1包含两个块,其起始地址为x和x+b,并且x mod 4b = 0,这意味着x也是L2中一个块的起始地址;因此,L2中的这个单一块包含了L1块x、x+b、x+2b和x+3b。假设处理器生成了对块y的引用,该块在两个缓存中都映射到包含x的块,因此发生了未命中。
由于L2也未命中,它将提取4b字节并替换掉包含x、x+b、x+2b和x+3b的块,而L1则提取b字节并替换掉包含x的块。由于L1仍然包含x+b,但L2不再包含,因此包含性属性不再成立。
为了在多块大小下维护包含性,当在较低层级进行替换时,我们必须查询更高层级的缓存,以确保在较低层级替换的任何字节在更高层级的缓存中被失效;不同层级的关联性会产生类似的问题。Baer和Wang(1988)详细描述了包含性的优点和挑战,并且在2017年,大多数设计师选择实现包含性,通常通过在所有缓存级别上统一块大小来实现。例如,Intel i7使用了L3的包含性,这意味着L3始终包含所有L2和L1的内容。这一决定使得i7能够在L3实现简单的目录方案,并将来自于L1和L2的干扰最小化,仅在目录指示L1或L2有缓存副本的情况下进行干扰。
相对而言,AMD Opteron使L2包含L1,但对L3没有这样的限制。它使用了一种监听协议,但只有在L2发生命中时才需要监听L1。
### 性能提升来自多处理和多线程
在本节中,我们简要探讨了一项关于在多核处理器(IBM Power5)上使用多线程的有效性研究;在下一节中,当我们检查英特尔i7的性能时,会再次回到这个话题。IBM Power5是一个支持同时多线程(SMT)的双核处理器;其基本架构与更近期的Power8(我们将在下一节中讨论)非常相似,但每个处理器只有两个核心。
为了考察多处理器中多线程的性能,研究人员在一台配备八个Power5处理器的IBM系统上进行了测量,每个处理器只使用一个核心。图5.25显示了8处理器Power5多处理器在有无同时多线程(SMT)情况下,对于SPECRate2000基准测试的加速比,如标题所述。平均而言,SPECintRate速度提升为1.23倍,SPECfpRate速度提升为1.16倍。需要注意的是,某些浮点基准测试在SMT模式下的性能略有下降,最大加速比降低至0.93。虽然人们可能会预期SMT能够更好地隐藏SPECFP基准测试较高的未命中率,但在此类基准下的SMT模式运行时,似乎遇到了内存系统的限制。

图5.25 在8处理器IBM eServer p5 575上,使用SPECfpRate(上半部分)和SPECintRate(下半部分)作为基准的SMT与单线程(ST)性能比较。注意,x轴从0.9的加速比开始,表示性能损失。每个Power5核心中只有一个处理器处于活动状态,这应该通过减少内存系统中的破坏性干扰来稍微改善SMT的结果。SMT结果是通过创建16个用户线程获得的,而ST结果仅使用八个线程;由于每个处理器仅有一个线程,Power5被操作系统切换到单线程模式。这些结果由IBM的John McCalpin收集。从数据可以看出,SPECfpRate的结果标准差高于SPECintRate(分别为0.13和0.07),这表明FP程序的SMT提升可能会有很大变化。
5.8 Putting It All Together: Multicore Processors and Their Performance
在大约10年的时间里,多核一直是提升性能的主要焦点,尽管其实现方式差异很大,对于更大规模的多芯片多处理器的支持也各不相同。在本节中,我们将研究三种不同多核的设计,它们对更大多处理器的支持以及一些性能特征,然后进行对小型到大型Xeon系统的更广泛评估,最后详细评估i7 920这一i7 6700的前身。
### 基于多核的多处理器在多程序工作负载下的性能
图5.26展示了三款为服务器应用设计的多核处理器的关键特性,这些处理器在2015至2017年间上市。Intel Xeon E7基于与i7相同的基本设计,但具有更多的核心、稍慢的时钟频率(功耗是限制因素)和更大的L3缓存。Power8是IBM Power系列中的最新款,拥有更多核心和更大的缓存。Fujitsu SPARC64 X+是最新的SPARC服务器芯片;与第三章提到的T系列不同,它采用了SMT。由于这些处理器被配置用于多核和多处理器服务器,因此它们作为一个家族提供,处理器数量、缓存大小等各不相同,如图所示。

图5.26 三款近期高端多核处理器(2015-2017年发布)的特性总结 该表展示了每个处理器系列中处理器数量、时钟频率和缓存大小的范围。Power8的L3采用非均匀缓存访问(NUCA)设计,并支持高达128 MiB的外部L4缓存,使用EDRAM技术。最近宣布了一款32核的Xeon处理器,但尚未有系统发货。Fujitsu SPARC64也提供8核设计,通常配置为单处理器系统。最后一行显示了具有已发布性能数据(如SPECintRate)的配置系统范围,其中包括处理器芯片数量和总核心数。Xeon系统包括通过额外逻辑扩展基本互连的多处理器;例如,使用标准的Quickpath互连将处理器数量限制为8,而最大系统为24个处理器(共192个核心),但SGI通过额外逻辑扩展互连(和一致性机制),提供了一个使用18核处理器芯片的32处理器系统,总规模为576个核心。这些处理器的新版本提高了时钟频率(在Power8的情况下显著,其他情况相对较少)和核心数量(在Xeon的情况下显著)。
这三种系统展示了连接片上核心和多个处理器芯片的一系列技术。首先,让我们看看核心在芯片内的连接方式。SPARC64 X+是最简单的:它在16个核心之间共享一个24路组相联的L2缓存。还有四个独立的DIMM通道,用于连接可通过一个16%的4开关访问的内存。


图5.27 显示了Power8和Xeon E7的芯片内部组织。Power8在L3缓存和CPU核心之间使用8条独立的总线。每个Power8还拥有两组链接,用于连接更大的多处理器系统。Xeon则使用三个环路连接处理器和L3缓存银行,并通过QPI进行芯片间链接。软件用于将一半的核心逻辑上关联到每个内存通道。
图5.27展示了Power8和Xeon E7芯片的组织结构。Power8中的每个核心都有一个直接连接的8 MiB L3缓存;其他缓存通过具有8条独立总线的互连网络访问。因此,Power8是真正的非均匀缓存架构(NUCA),因为访问所连接的L3缓存的时间要比访问其他L3缓存快得多。每个Power8芯片都有一组链接,可用于构建大型多处理器系统,我们很快会看到这种组织方式。内存链接连接到一个特殊的内存控制器,该控制器包括一个L4,并直接与DIMM接口。
图5.27 B部分展示了当Xeon E7处理器芯片中有18个或更多核心(该图显示20个核心)时的组织结构。
三个环路连接核心和L3缓存银行,每个核心和每个L3缓存银行都连接到两个环路。因此,任何缓存银行或核心都可以通过选择合适的环路从其他核心访问。因此,在芯片内部,E7具有均匀的访问时间。然而,在实际操作中,E7通常以NUMA架构运行,通过将一半核心逻辑上关联到每个内存通道来增加所需内存页在特定访问时可用的概率。E7提供3个QuickPath互连(QPI)链接,用于连接多个E7处理器。
由这些多核处理器组成的多处理器系统使用多种不同的互连策略,如图5.28所示。Power8设计支持连接16个Power8芯片,总共192个核心。组内链接为完全连接的4个处理器芯片模块提供更高带宽的互连。组间链接用于将每个处理器芯片连接到其他3个模块。因此,每个处理器与任何其他处理器之间相距两跳,内存访问时间取决于地址是位于本地内存、集群内存还是跨集群内存(实际上后者可以有两个不同的值,但差异被跨集群时间所掩盖)。
Xeon E7使用QPI将多个多核心芯片互连。在一个由4个芯片组成的多处理器系统中,结合最新发布的Xeon,可以达到128个核心,每个处理器上的三个QPI链接与三个邻近处理器相连,从而形成一个4芯片完全连接的多处理器系统。由于内存直接连接到每个E7多核芯片,即使在这个4芯片的安排中,也存在非均匀内存访问时间(本地与远程)。图5.28展示了如何连接8个E7处理器;与Power8类似,这种连接方式使得每个处理器与其他处理器之间只需一到两跳。许多基于Xeon的多处理器服务器拥有超过8个处理器芯片。在这种设计中,典型的组织结构是将4个处理器芯片以方形模块连接在一起,每个处理器与两个邻居相连。每个芯片的第三个QPI连接到交叉开关。这种方式可以构建非常大的系统。内存访问可以发生在四个位置,具有不同的时延:处理器本地、立即邻居、距离两跳的集群邻居以及通过交叉开关访问。其他组织结构也是可能的,这样可以少用一个完整的交叉开关,以换取更多跳数来访问远程内存。


SPARC64 X+也使用4处理器模块,但每个处理器与其邻近的处理器有三个连接,以及两个(或在最大配置中三个)连接到交叉开关。在最大配置中,可以将64个处理器芯片连接到两个交叉开关,总共可达1024个核心。内存访问为NUMA(本地、模块内和通过交叉开关),一致性是基于目录的。
### 多核基础多处理器在多程序工作负载下的性能
首先,我们使用SPECintRate比较这三款多核处理器的性能可扩展性,考虑最多64个核心的配置。图5.29展示了性能相对于最小配置的可扩展性,最小配置在4到16个核心之间变化。在图中,假设最小配置具有完美的加速(即8个核心为8,12个核心为12,等等)。该图并未显示不同处理器之间的性能对比。实际上,这些性能差异显著:在4核配置中,IBM Power8的每核心性能是SPARC64 X+的1.5倍!相反,图5.29展示了随着额外核心的增加,每个处理器系列的性能如何变化。
在这三款处理器中,有两款在扩展到64个核心时表现出收益递减的趋势。Xeon系统在56和64核心时似乎表现出最大的性能下降。这可能主要是因为更多核心共享较小的L3缓存。例如,40核系统使用4个芯片,每个芯片有60 MiB的L3缓存,这样每个核心分配到6 MiB的L3缓存。而56核和64核系统也使用4个芯片,但每个芯片只有35或45 MiB的L3缓存,导致每个核心只有2.5–2.8 MiB的L3缓存。结果,较大的L3未命中率可能导致56核和64核系统的加速性能下降。

图5.29 四款多核处理器在SPECintRate基准测试中的性能可扩展性 图5.29展示了当核心数量增加到64时,四款多核处理器在SPECintRate基准测试中的性能可扩展性。每个处理器的性能相对于最小配置进行绘制,并假设该配置具有完美的加速。尽管该图显示了特定多处理器在增加核心时的可扩展性,但并未提供不同处理器之间的性能数据。在同一处理器系列内部,时钟频率存在差异。这些差异通常被核心扩展效应所掩盖,除了Power8在从最小配置到64核配置时,其时钟频率范围变化为1.5%。
IBM Power8的结果也很不寻常,表现出显著的超线性加速。然而,这一效应主要是由于时钟频率的差异,在Power8处理器之间的差异远大于图中其他处理器。特别是,64核配置的时钟频率最高(4.4 GHz),而4核配置的时钟频率为3.0 GHz。如果我们根据与4核系统的时钟频率差异对64核系统的相对加速进行归一化,则有效加速为57,而不是84。因此,虽然Power8系统的可扩展性良好,可能在这些处理器中表现最佳,但并非奇迹。
图5.30展示了这三款系统在超过64个处理器配置下的可扩展性。同样,时钟频率差异解释了Power8的结果;在192个处理器时,考虑到时钟频率差异后的等效加速为167,而未考虑时钟频率差异时为223。即使是167,Power8的可扩展性仍然优于SPARC64 X+或Xeon系统。令人惊讶的是,尽管从最小系统扩展到64核时,加速效果受到一些影响,但在这些更大配置下似乎并没有显著恶化。这种工作负载的特性高度并行且用户-CPU密集,而且在扩展到64核时所付出的开销可能导致了这一结果。

图5.30 多处理器多核的相对性能可扩展性。与之前一样,性能是相对于最小可用系统进行展示的。80核的Xeon结果与较小配置中出现的L3效应相同。所有大于80核的系统每个核心的L3缓存在2.5到3.8 MiB之间,而80核或更小的系统每个核心则有6 MiB的L3缓存。
### Xeon MP在不同工作负载下的可扩展性
在本节中,我们关注Xeon E7多处理器在三种不同工作负载下的可扩展性:基于Java的商业工作负载、虚拟机工作负载和科学并行处理工作负载,所有这些均来自SPEC基准测试组织,具体描述如下。
- **SPECjbb2015**:模拟一个超市IT系统,该系统处理混合的销售点请求、在线购买和数据挖掘操作。性能指标侧重于吞吐量,我们使用运行多个Java虚拟机的服务器端的最大性能测量。
- **SPECVirt2013**:模拟一组独立运行其他SPEC基准的虚拟机,包括CPU基准、Web服务器和邮件服务器。该系统必须为每个虚拟机满足服务质量保证。
- **SPECOMP2012**:由14个使用OpenMP标准编写的科学和工程程序组成,适用于共享内存的并行处理。这些代码使用Fortran、C和C++编写,涵盖从流体动力学到分子建模再到图像处理等多个领域。
与之前的结果一样,图5.31显示了假设最小配置(对于这些基准测试,范围从48核到72核)线性加速的性能,并将性能相对于该最小配置进行绘制。SPECjbb2015和SPECVirt2013包括重要的系统软件,包括Java虚拟机软件和虚拟机监控程序。除了系统软件外,进程之间的交互非常小。相比之下,SPECOMP2012是一个真正的并行代码,多个用户进程共享数据并协作进行计算。

图5.31 展示了在一系列Xeon E7系统上的性能扩展,显示相对于最小基准配置的性能,并假设该配置获得完美的加速(例如,最小的SPECOMP配置为30个核心,我们假设该系统的性能为30)。从这些数据中只能评估相对性能,基准测试之间的比较没有相关性。请注意垂直轴和水平轴的比例差异。
让我们首先看看SPECjbb2015。它在最大的配置中获得了78%到95%之间的加速效率(加速/处理器比),显示出良好的加速性能。SPECVirt2013的表现更佳(在测量的系统范围内),在192核时几乎实现了线性加速。SPECjbb2015和SPECVirt2013都是随着系统规模增大而扩展应用程序大小的基准测试(如第1章讨论的TPC基准),因此阿姆达尔定律和进程间通信的影响较小。
最后,让我们来看SPECOMP2012,这是这些基准测试中计算密集度最高的一个,真正涉及并行处理。这里可见的主要趋势是,当我们从30个核心扩展到576个核心时,效率稳步下降,以至于在576个核心时,系统的效率仅为30个核心时的一半。这一效率降低导致相对加速比为284,假设30个核心时的加速比为30。这可能是由于有限的并行性以及同步和通信开销所导致的阿姆达尔定律效应。与SPECjbb2015和SPECVirt2013不同,这些基准测试并没有针对更大系统进行扩展。
**Intel i7 920多核处理器的性能与能效**
在本节中,我们仔细研究了i7 920的性能,作为6700的前身,我们关注了第三章中讨论的两组基准测试:并行Java基准和并行PARSEC基准(详细描述见247页的图3.32)。尽管本研究使用的是较旧的i7 920,但它仍然是关于多核处理器能效及多核与SMT结合效果的最全面研究。i7 920和6700的相似性表明,基本的洞察力也适用于6700。
首先,我们观察不使用SMT时多核性能与单核性能的扩展情况。接着,我们结合多核和SMT的能力。本节中的所有数据与早期的i7 SMT评估(第三章)相同,数据集与之前使用的一致(见247页的图3.32),只不过删除了Java基准中的tradebeans和pjbb2005(仅保留五个可扩展的Java基准);因为tradebeans和pjbb2005即使在四个核心和八个线程下也从未实现超过1.55的加速,因此不适合用于评估更多核心的性能。

**图5.32** 此图显示了在不使用SMT的情况下,二核和四核执行并行Java和PARSEC工作负载的加速比和能效。这些数据由Esmaeilzadeh等人(2011年)收集,使用的是与第三章中描述的相同设置。Turbo Boost被关闭。加速比和能效使用调和平均数进行总结,意味着在2个核心上运行每个基准测试所花费的总时间是等效的。
图5.32绘制了Java和PARSEC基准在不使用SMT时的加速比和能效。能效通过以下比例计算:单核运行所消耗的能量除以双核或四核运行所消耗的能量(即,效率是能量消耗的倒数)。更高的能效意味着处理器在进行相同计算时消耗更少的能量,值为1.0时表示盈亏平衡点。在所有情况下,未使用的核心处于深度睡眠模式,这最大程度地降低了它们的功耗,基本上将其关闭。在比较单核和多核基准的数据时,重要的是要记住,在单核(以及多核)情况下,L3缓存和内存接口的全部能量成本都是要支付的。这一事实增加了对于合理扩展的应用程序,能量消耗改善的可能性。调和平均数用于总结结果,并在说明中做出相关解释。
如图所示,PARSEC基准测试的加速比优于Java基准测试,在四核上实现了76%的加速效率(即,实际加速除以处理器数量),而Java基准测试在四核上实现了67%的加速效率。尽管这一观察从数据中显而易见,但分析为何存在这种差异是困难的。很可能是因为Amdahl法则的影响,降低了Java工作负载的加速比,因为它包含一些典型的串行部分,例如垃圾回收器。此外,处理器架构与应用程序之间的交互也可能发挥作用,这会影响同步或通信成本等问题。特别是,像PARSEC中的一些良好并行化的应用程序,有时会受益于计算与通信之间的有利比例,从而减少对通信成本的依赖(见附录I)。
这些加速差异转化为能效差异。例如,PARSEC基准测试的能效实际上比单核版本略有改善;这个结果可能受益于L3缓存在多核运行中比在单核情况下更有效地使用,而在两种情况下的能量成本是相同的。因此,对于PARSEC基准测试,多核方法实现了设计者在从ILP聚焦设计转向多核设计时所希望的目标;即,它的性能扩展速度与功耗扩展一样快,甚至更快,从而实现恒定或甚至改善的能效。在Java的情况下,我们看到无论是二核还是四核运行都未能实现能效的盈亏平衡,因为Java工作负载的加速水平较低(尽管2p运行的Java能效与PARSEC相同)。四核Java案例中的能效相当高(0.94)。很可能,专注于ILP的处理器需要更多的功率,以在PARSEC或Java工作负载上实现可比的加速。因此,TLP中心的方法在提高这些应用程序的性能方面显然优于ILP中心的方法。正如我们将在5.10节中看到的,对于多核的简单、高效和长期扩展,仍然有理由持悲观态度。
### 将多核和超线程结合起来
最后,我们考虑通过对两个到四个处理器和一个到两个线程的两组基准测试进行测量,来研究多核和多线程的结合(总共四个数据点,最多八个线程)。图5.33展示了在Intel i7处理器上,当处理器数量为二或四且是否使用超线程时所获得的加速比和能效,使用调和平均数来总结这两组基准测试。显然,当存在足够的线程级并行性时,即使在多核情况下,超线程也可以提升性能。例如,在四核无超线程的情况下,Java和PARSEC的加速效率分别为67%和76%。而在四核启用超线程的情况下,这些比率则惊人地达到了83%和97%。

图5.33 该图展示了在启用和禁用超线程的情况下,双核和四核并行执行Java和PARSEC工作负载的加速比。请记住,之前的结果在线程数量上从两个到八个变化,并反映了架构效应和应用特性。正如图5.32的说明中所讨论的,使用调和平均数来总结结果。
能效呈现出略微不同的情况。在PARSEC的情况下,四核超线程(八个线程)的加速几乎是线性的,而功耗的增加速度更慢,导致该情况下的能效为1.1。Java的情况则更加复杂;在两核超线程(四个线程)运行时,能效达到峰值0.97,而在四核超线程(八个线程)运行时降至0.89。这表明,当部署超过四个线程时,Java基准测试很可能遇到了阿姆达尔定律的影响。正如一些架构师所观察到的,多核确实将更多的性能(因此也是能效)的责任转移给了程序员,而Java工作负载的结果无疑证明了这一点。
5.9 Fallacies and Pitfalls
鉴于我们对并行计算理解的不足,存在许多隐藏的陷阱,这些陷阱可能会被细心的设计师发现,也可能会让不幸的设计者遭遇麻烦。考虑到多处理器技术多年来所伴随的大量炒作,常见的误区层出不穷。我们在此列出了一些典型的误区。
陷阱 通过线性加速比与执行时间来衡量多处理器的性能。
像图5.32和图5.33那样绘制性能与处理器数量关系的图表,显示线性加速、平稳期以及随后下降,长期以来一直被用来评估并行处理器的成功。尽管加速比是并行程序的一个方面,但它并不是性能的直接衡量标准。首要问题在于所使用处理器的性能范围:一个线性提高性能以达到相当于100个Intel Atom处理器(用于上网本的低端处理器)的程序,可能比在8核Xeon上运行的版本还要慢。对于浮点密集型程序尤其要小心;没有硬件支持的处理单元可能在扩展时表现良好,但整体性能却较差。
仅当你比较每台计算机上最佳算法时,比较执行时间才算公平。在两台计算机上比较相同的代码似乎是公平的,但实际上并非如此;并行程序在单处理器上的运行速度可能比顺序版本更慢。开发并行程序有时会导致算法的改进,因此将之前已知的最佳顺序程序与并行代码进行比较——这看似公平——但并不能比较等效算法。为了解决这个问题,有时使用“相对加速比”(相同程序)和“真实加速比”(最佳程序)这两个术语。
结果如果显示出超线性性能,即在n个处理器上运行的程序比等效的单处理器快超过n倍,这可能表明比较是不公平的,尽管也有一些情况下确实遇到过“真实”的超线性加速。例如,一些科学应用在处理器数量从2或4增至8或16时,通常会实现超线性加速。这些结果通常是因为在2或4个处理器的多处理器系统中不适合进入的关键数据结构,能够适配8或16个处理器的汇总缓存。正如我们在前一部分看到的,其他差异(比如高时钟频率)在比较略有不同的系统时也可能表现出超线性加速。
总之,通过比较加速比来评估性能至多是棘手的,至于误导则更为严重。比较两种不同多处理器的加速比并不一定能告诉我们这些多处理器的相对性能,正如我们在前一部分所看到的。即使是在同一多处理器上比较两种不同的算法也是棘手的,因为我们必须使用真实加速比,而不是相对加速比,以获得有效的比较。
### 谬论:阿姆达尔定律不适用于并行计算机
1987年,一家研究机构的负责人声称,MIMD多处理器打破了阿姆达尔定律。然而,这并不意味着该定律已被并行计算机推翻;程序中被忽略的部分仍然会限制性能。为了理解媒体报道的基础,我们来看一下阿姆达尔(1967年)最初所说的话:
“此时可以得出一个相当明显的结论:除非在顺序处理速率上取得几乎相同量级的成就,否则为实现高并行处理速率所付出的努力是浪费。” [第483页]
对该定律的一种解释是,由于每个程序的某些部分必须是顺序执行的,因此有效的经济处理器数量是有限的——比如说100个。通过展示1000个处理器的线性加速,这种对阿姆达尔定律的解释被证伪。
声明阿姆达尔定律已被“克服”的依据是使用了规模加速,也称为弱扩展。研究人员将基准测试的 数据集规模扩大到1000倍,并比较了单处理器和并行执行的时间。对于这个特定算法,程序的顺序部分在输入大小独立时是恒定的,其余部分则是完全并行的——因此,在1000个处理器上实现了线性加速。然而,由于运行时间的增长速度超过了线性,即使在有1000个处理器的情况下,经过缩放后程序的运行时间实际上也变得更长。
假设输入规模扩展的加速与真正的加速并不相同,将其报告为相同是具有误导性的。由于并行基准测试通常在不同规模的多处理器上运行,因此重要的是要明确允许什么类型的应用扩展,以及如何进行这种扩展。虽然简单地根据处理器数量扩展数据大小很少合适,但对更大处理器数量假设固定问题大小(称为强扩展)往往也不合适,因为用户在面对更大的多处理器时很可能会选择运行更大或更详细的应用版本。有关这一重要主题的更多讨论,请参见附录I。
### 谬论:线性加速是实现多处理器成本效益的必要条件
人们普遍认为,并行计算的主要优点之一是提供比最快的单处理器更短的解决时间。然而,许多人也认为,除非并行处理器能够实现完美的线性加速,否则它们无法与单处理器在成本效益上相提并论。这个论点认为,由于多处理器的成本是处理器数量的线性函数,任何低于线性加速的表现都会导致性能/成本比下降,从而使并行处理器的成本效益低于单处理器。
这个论点的问题在于,成本不仅仅是处理器数量的函数,还取决于内存、输入/输出以及系统的开销(机箱、电源、互连等)。在多核时代,这种观点更显得不合理,因为每个芯片上都有多个处理器。
Wood和Hill(1995)指出了将内存纳入系统成本的影响。我们使用基于最新数据的TPC-C和SPECRate基准测试的例子来说明这一点,但同样的论点也适用于并行科学应用工作负载,这可能会使案例更为有力。

图5.34显示了在配置了4至64个处理器的IBM eServer p5多处理器上TPC-C、SPECintRate和SPECfpRate的加速比。图中显示,只有TPC-C的加速比优于线性加速。对于SPECintRate和SPECfpRate,加速比低于线性,但成本也是如此,因为与TPC-C不同,所需的主内存和磁盘容量的增长都低于线性。如图5.35所示,处理器数量更大的配置实际上可能比4处理器配置更具成本效益。在比较两台计算机的成本效益时,我们必须确保包括对总系统成本和可实现性能的准确评估。对于许多内存需求较大的应用程序来说,这样的比较可以极大地提高使用多处理器的吸引力。

图5.35显示了IBM eServer p5多处理器系统在4至64个处理器之间的性能/成本表现,相对于4处理器配置。任何高于1.0的测量结果都表明该配置比4处理器系统更具成本效益。对于所有三个基准测试,8处理器配置都显示出优势,而三个基准测试中的两个显示16处理器和32处理器配置在成本性能方面具有优势。对于TPC-C,这些配置是官方运行中使用的配置,这意味着随着处理器数量的增加,磁盘和内存的扩展几乎呈线性增长,64处理器机器的成本大约是32处理器版本的两倍。相比之下,磁盘和内存的扩展速度较慢(尽管仍然比在64处理器上实现最佳SPECrate所需的速度要快)。在……方面
特别是,磁盘配置从4处理器版本的1个驱动器增加到64处理器版本的4个驱动器(每个驱动器140GB)。内存从4处理器系统的8GB增加到64处理器系统的20GB。
陷阱 没有针对或优化多处理器体系结构开发软件。
在多处理器领域,软件一直落后于硬件,这可能是因为软件问题更加复杂。我们举一个例子来说明这个问题的微妙之处,但还有很多例子可以选择。
一个常见的问题是将为单处理器设计的软件移植到多处理器环境中。例如,2000年的SGI操作系统最初使用单个锁来保护页表数据结构,因为它假设页分配不频繁。在单处理器环境中,这并不构成性能问题。但在多处理器环境中,对于某些程序而言,它可能会成为主要的性能瓶颈。
考虑一个在启动时分配大量页面的程序,UNIX会为静态分配的页面使用页表数据结构。假设该程序被并行化,多个进程分配页面。因为每当页表数据结构被使用时,都会锁定它,所以即使操作系统允许多个线程同时运行,也会被序列化。这些进程都试图同时为其页面分配内存(这正是我们在初始化时所期望的)。
本页表序列化消除了初始化过程中的并行性,并对整体并行性能产生了显著影响。即使在多任务环境下,这种性能瓶颈也依然存在。例如,假设我们将并行程序分割成独立的进程,并在每个处理器上运行一个进程,以避免进程之间的共享(这正是一位用户所做的,因为他合理地认为性能问题源于他应用程序中的无意共享或干扰)。不幸的是,锁仍然会将所有进程序列化,因此即使在多任务环境下的性能也很差。这个陷阱表明,当软件在多处理器上运行时,可能会出现一些微妙但显著的性能问题。像许多其他关键软件组件一样,操作系统算法和数据结构必须在多处理器环境中重新考虑。将锁放置在页表的较小部分上可以有效地消除这个问题。在没有实际共享的情况下,内存结构中也存在类似的问题,这会增加一致性通信量。随着多核成为从台式机到服务器等各种设备的主导主题,这些问题变得越来越重要。在服务器领域,对并行软件的不足投资变得显而易见。由于缺乏关注,我们可能需要很多年才能充分利用日益增多的处理器核心。
5.10 The Future of Multicore Scaling
在过去的30多年里,研究人员和设计师一直预测单处理器的终结以及多处理器的主导地位。直到本世纪初,这一预测一直被不断证伪。正如我们在第三章中看到的,寻找和利用更多指令级并行性(ILP)的成本在效率上变得不可承受(无论是在硅面积还是功率方面)。当然,多核并不能神奇地解决功率问题,因为它显然增加了晶体管数量和切换的活跃晶体管数量,这两者都是功率的主要贡献因素。正如我们将在本节中看到的,能源问题可能会比之前认为的更严重地限制多核扩展。
ILP扩展失败是由于可用ILP的限制以及利用这些ILP的效率。同样,两种因素的结合意味着通过增加核心简单地扩展性能不太可能广泛成功。这种结合源于阿姆达尔法则带来的挑战,该法则评估了利用并行性的效率,以及丹纳德缩放的终止,后者决定了多核处理器所需的能量。
为了理解这些因素,我们采用一个简单的技术缩放模型(基于Esmaeilzadeh等人(2012)中的广泛且高度详细的分析)。让我们先回顾一下CMOS中的能量消耗和功率。回想一下第一章,切换一个晶体管所需的能量为:
![]()
CMOS缩放的限制主要是由热功率决定的,热功率是静态泄漏功率和动态功率的结合,后者往往占主导地位。功率的计算公式为:

为了理解能量和功率缩放的影响,我们将今天的22纳米技术与预计在2021-2024年可用的技术进行比较(具体取决于摩尔定律放缓的速度)。图5.36展示了基于技术预测及其对能量和功率缩放影响的比较。注意,功率缩放大于1.0意味着未来设备消耗更多功率;在这种情况下,为1.79倍。

图5.36 比较了2016年的22纳米技术与预计在2022年至2024年之间可用的未来11纳米技术。11纳米技术的特性基于国际半导体技术路线图,该路线图最近因摩尔定律的延续性及其缩放特性的不确定性而停止更新。
考虑一下这对最新的Intel Xeon处理器E7-8890的影响,该处理器具有24个核心,72亿个晶体管(包括近70 MiB的缓存),工作频率为2.2 GHz,热功率额定值为165瓦,芯片面积为456 mm²。时钟频率已经受到功率消耗的限制:4核版本的时钟为3.2 GHz,10核版本的时钟为2.8 GHz。使用11纳米技术,相同大小的芯片将容纳96个核心,几乎有280 MiB的缓存,并以4.9 GHz的时钟频率运行(假设完美的频率缩放)。不幸的是,在所有核心都运行且没有效率改善的情况下,它将消耗165%的1.79,即295瓦。如果我们假设165瓦的热失效限制保持不变,那么最多只能激活54个核心。这个限制在5到6年的时间跨度内实现的最大性能提升为54/24=2.25,低于1990年代末看到的性能缩放的一半。此外,正如下一个例子所示,我们可能还会遇到阿姆达尔法则的影响。
例子 假设我们有一个96核的未来一代处理器,但平均只有54个核心可以被忙用。假设90%的时间,我们可以使用所有可用的核心;9%的时间,我们可以使用50个核心;1%的时间是严格的串行执行。我们可以期待多大的加速?假设在不使用时核心可以关闭,不消耗电力,并且假设不同数量的核心的使用是分布式的,因此我们只需关注平均功耗。多核加速与99%时间可以使用所有处理器的24核版本相比如何?
回答 我们可以找出在90%时间内,当可用核心超过54个时,可以使用多少个核心,如下所示:

在考虑功率限制和阿姆达尔法则效应时,96核版本的加速效果不到24核版本的两倍。实际上,时钟频率的提升所带来的加速几乎与4%处理器数量增加所带来的加速相匹配。我们将在结论部分进一步讨论这些问题。
5.11 Concluding Remarks
在前一节中我们看到,多核并不能神奇地解决功率问题,因为它明显增加了晶体管数量和活动晶体管的切换数量,这两者是功率的主要来源。丹纳德缩放的失效使得这一情况更加极端。
然而,多核确实改变了游戏规则。通过将空闲核心置于省电模式,可以实现一定的功率效率改进,本章的结果显示了这一点。例如,在Intel i7中关闭某些核心可以让其他核心在Turbo模式下运行。这种能力允许在更高时钟频率与更少处理器之间,以及更多处理器与较低时钟频率之间进行权衡。
更重要的是,多核通过更多依赖线程级并行性(TLP)来转移保持处理器忙碌的负担,而TLP是应用程序和程序员负责识别的,反之指令级并行性(ILP)则由硬件负责。避免阿姆达尔法则效应的多程序和高度并行工作负载会更容易受益。
尽管多核在能源效率挑战上提供了一些帮助,并将很多负担转移到软件系统上,但仍然存在困难的挑战和未解决的问题。例如,利用线程级版本的激进猜测的尝试迄今为止遇到了与ILP相似的命运。也就是说,性能提升有限,可能还不及能耗的增长,因此诸如猜测线程或硬件提前执行等理念尚未成功融入处理器中。与ILP的猜测一样,除非猜测几乎总是正确,否则成本超过收益。
因此,目前看来,某种形式的简单多核扩展不太可能提供一种具有成本效益的性能增长路径。必须克服一个基本问题:以节能和硅高效的方式找到并利用大量的并行性。在前一章中,我们考察了通过SIMD方法利用数据并行性。在许多应用中,数据并行性大规模存在,SIMD是一种更具能效的数据并行性利用方法。在下一章中,我们探讨大规模云计算。在这样的环境中,来自个别用户的数百万个独立任务提供了大量的并行性。阿姆达尔法则对这些系统的规模限制作用微乎其微,因为任务(例如,数百万个谷歌搜索请求)是独立的。最后,在第七章中,我们探讨了领域特定架构(DSA)的兴起。大多数领域特定架构利用目标领域的并行性,通常是数据并行性,正如GPU一样,DSA在能源消耗或硅利用率方面能够实现更高的效率。
在2012年发布的最后一版中,我们提出了考虑异构处理器是否值得的问题。当时,没有这种多核处理器被交付或宣布,异构多处理器仅在专用计算机或嵌入式系统中取得有限成功。尽管编程模型和软件系统仍然具有挑战性,但异构处理器的多处理器不可避免地将在未来发挥重要作用。将领域特定处理器(如第四章和第七章讨论的处理器)与通用处理器结合,或许是实现性能和能效提升,同时保持通用处理器所提供的一部分灵活性的最佳途径。
6 Warehouse-Scale Computers to Exploit Request-Level and Data-Level Parallelism
数据中心就是计算机。
—— Luiz Andre Barroso, Google (2007)
一百年前,公司们停止使用蒸汽机和发电机自给自足,转而接入新建的电网。电力公司提供的廉价电力不仅改变了商业运作方式,还引发了一系列经济和社会变革,造就了现代世界。如今,一场类似的革命正在进行中。连接到互联网全球计算网络的大型信息处理中心开始将数据和软件代码源源不断地输送到我们的家庭和企业。这一次,计算正在变成一种公用事业。
—— Nicholas Carr,《大切换:从爱迪生到谷歌的全球重构》(2008)
6.1 Introduction
任何人都可以打造一款快速的 CPU,关键在于构建一个快速的系统。
—— Seymour Cray,被认为是超级计算机之父
仓库级计算机(WSC)是数十亿人每天使用的互联网服务的基础:搜索、社交网络、在线地图、视频分享、在线购物、电子邮件服务等等。这些互联网服务的巨大普及催生了能够满足公众快速需求的 WSC。尽管 WSC 表面上看似大型数据中心,但它们的架构和运作却截然不同,正如我们将要看到的。如今的 WSC 作为一台巨大的机器,建设成本高达数亿美元,涵盖电力和冷却基础设施、服务器以及连接和容纳 5 万到 10 万台服务器的网络设备。此外,商业云计算的快速增长(见第 6.5 节)使得任何持有信用卡的人都能使用 WSC。
计算机架构自然延伸到 WSC 的设计。例如,谷歌的 Luiz Barroso(前文引用)在计算机架构方面进行了博士论文研究。他认为,设计规模、确保可靠性以及调试硬件的能力对 WSC 的创建和运作非常有帮助。
在这个需要创新的前沿规模中,涉及电力分配、冷却、监控和运营等方面,WSC 是现代超级计算机的后代——这使得 Seymour Cray 成为当今 WSC 架构师的教父。他的极端计算机处理的是其他地方无法完成的计算,但由于成本极高,只有少数公司能够负担得起。这一次,目标是为世界提供信息技术,而不是为科学家和工程师提供高性能计算(HPC);因此,WSC 在当今社会中无疑比 Cray 的超级计算机在过去所起的作用更为重要。
毫无疑问,WSC 的用户数量比高性能计算多出几个数量级,并且它们在 IT 市场中占据了更大的份额。无论是从用户数量还是收入来看,谷歌的规模都是 Cray Research 的 1000 倍。
WSC 架构师与服务器架构师有许多共同的目标和要求:
- **成本效益**——每美元所完成的工作至关重要,这在很大程度上是由于规模的原因。将一组 WSC 的成本降低几个百分点可能节省数百万美元。
- **能效**——除了从 WSC 中释放出的光子外,它们本质上是封闭系统,几乎所有消耗的能量都转化为必须移除的热量。因此,峰值功率和实际消耗的电力影响着电力分配和冷却系统的成本。构建 WSC 的基础设施成本的主要部分用于电力和冷却。此外,能效也是环境管理的重要组成部分。因此,每焦耳所完成的工作对 WSC 及其服务器来说至关重要,因为构建计算机仓库的电力和机械基础设施的高成本以及随之而来的每月公用事业账单。
- **通过冗余确保可靠性**——互联网服务的长期运行性质意味着 WSC 中的硬件和软件必须共同提供至少 99.99%(称为“四个九”)的可用性;也就是说,服务每年停机时间必须少于 1 小时。冗余是确保 WSC 和服务器可靠性的关键。尽管服务器架构师通常会利用更多硬件以更高成本实现高可用性,但 WSC 架构师则依赖数量众多且具成本效益的服务器,通过网络连接并由软件管理冗余。除了 WSC 内部的本地冗余外,组织还需要冗余的 WSC 来掩盖可能导致整个 WSC 停机的事件。实际上,虽然每个云服务的可用性需求至少为 99.99%,但像亚马逊、谷歌或微软这样的完整互联网公司的可靠性需求甚至更高。如果这些公司中的任何一个每年完全离线 1 小时,即 99.99% 的可用性,那将成为头条新闻。多个 WSC 还有额外的好处,能够降低广泛部署服务的延迟(见图 6.18–6.20)。
- **网络 I/O**——服务器架构师必须为外部世界提供良好的网络接口,WSC 架构师也必须如此。网络连接是保持多个 WSC 之间数据一致性以及与公众接口所必需的。
- **交互式和批处理工作负载**——虽然对于拥有数十亿用户的搜索和社交网络等服务,预期会有高度交互式的工作负载,但 WSC 与服务器一样,也运行大规模并行批处理程序,以计算对这些服务有用的元数据。例如,运行 MapReduce 作业将从网络爬虫返回的页面转换为搜索索引(见第 6.2 节)。
不出所料,WSC(大规模计算中心)架构与服务器架构之间也存在一些不同的特征:
- **充足的并行性**——服务器架构师关心的是目标市场中的应用程序是否有足够的并发性来证明大量并行硬件的合理性,以及是否需要高昂的通信硬件成本来利用这种并行性。而 WSC 架构师则没有这样的顾虑。首先,批处理应用程序受益于大量独立的数据集,这些数据集需要独立处理,例如来自网络爬虫的数十亿个网页。这种处理属于数据级并行性,我们在第四章中见过,这次是应用于存储中的数据,而不是内存中的数据。其次,交互式互联网服务应用程序(也称为软件即服务,SaaS)可以从数百万个独立用户的交互式互联网服务中受益。在 SaaS 中,读写操作通常不相互依赖,因此很少需要同步。例如,搜索使用只读索引,而电子邮件通常读取和写入独立的信息。我们将这种易于实现的并行性称为请求级并行性,因为许多独立的操作可以自然地并行进行,几乎不需要通信或同步;例如,基于日志的更新可以减少吞吐量需求。即使是读/写依赖的特性,有时也会被放弃,以提供能够扩展到现代 WSC 尺寸的存储。在任何情况下,WSC 应用程序别无选择,只能找到能够在数百到数千台服务器上扩展的算法,因为这正是客户的期望,也是 WSC 技术所提供的。
- **运营成本重要**——服务器架构师通常忽视服务器的运营成本,认为与购买成本相比,这些成本微不足道。而 WSC 的使用寿命更长——建筑、电力和冷却基础设施通常要摊销 10 到 15 年——因此运营成本不断增加:能源、配电和冷却在 10 年内占 WSC 成本的 30%以上。
- **选址重要**——建设 WSC 的第一步是建立一个仓库。一个问题是在哪里?房地产经纪人强调位置,但 WSC 的位置意味着需接入水源、便宜的电力、靠近互联网主干光缆、有附近的人来工作,以及低风险的环境灾害(如地震、洪水和飓风)。更明显的关注点是土地成本,包括足够的空间来扩展 WSC。对于拥有多个 WSC 的公司,另一个关注点是寻找地理上靠近当前或未来互联网用户群体的地方,以减少互联网延迟。其他因素包括税收、物业成本、社会问题(人们有时希望在自己的国家设立设施)、政治问题(某些司法管辖区要求本地托管)、网络成本、网络可靠性、能源成本、能源来源(例如,水电与煤电)、天气(较凉爽的地区成本更低,如第 6.4 节所示)以及整体互联网连接性(澳大利亚地理上接近新加坡,但两者之间的网络链接带宽并不理想)。
- **低利用率下高效计算**——服务器架构师通常在成本预算内设计系统以达到峰值性能,并仅担心电力,以确保不超过其机箱的冷却能力。如我们所见(图 6.3),WSC 服务器很少能够完全利用,部分原因是为了确保低响应时间,部分原因是提供所需的冗余以实现可靠计算。鉴于运营成本的重要性,这些服务器在所有利用率水平上都需要高效计算。
■ 规模及其相关的机会/问题——极端计算机通常非常昂贵,因为它们需要定制硬件,而由于极端计算机的生产数量较少,定制成本无法有效摊销。然而,当一次购买数千台服务器时,可以享受巨大的批量折扣。WSC(大规模计算中心)内部规模庞大,即使WSC的数量不多,仍然存在规模经济。正如我们在第6.5节和第6.10节中将看到的,这些规模经济导致了商业云计算的出现,因为WSC的单位成本较低,使得公司能够以低于外部人士自行租用服务器的成本进行租赁。10万台服务器的另一面是故障。

图6.1 列出了一个新集群(2400台服务器)第一年内的停机和异常情况及其大致发生频率。我们将Google所称的集群标记为阵列;参见图6.5。数据来源于Barroso, L.A. (2010) 的《仓库规模计算》[主题演讲],发表于2010年6月8日至10日的ACM SIGMOD会议,印第安纳波利斯,印第安纳州。
图6.1显示了2400台服务器的停机和异常情况。即使一台服务器的平均故障时间(MTTF)达到惊人的25年(200,000小时),WSC架构师仍需设计以应对每天五次服务器故障。图6.1列出了年化磁盘故障率为2%到10%。考虑到每台服务器有两个磁盘,年故障率为4%,在10万台服务器的情况下,WSC架构师应该预计每小时会发生一次磁盘故障。然而,软件故障远远超过硬件故障,如图6.1所示,因此系统设计必须具备抵御因软件错误导致的服务器崩溃的能力,这种崩溃发生的频率甚至高于磁盘故障。在这些大型设施中拥有数千台服务器后,WSC运营商在更换磁盘方面变得非常熟练,因此WSC的磁盘故障成本远低于小型数据中心。同样的道理也适用于DRAM。如果有更便宜的组件可用,WSC可能会使用更不可靠的组件。
### 示例
计算图6.1中2400台服务器上运行的服务的可用性。与真实的WSC中的服务不同,在此示例中,该服务无法容忍硬件或软件故障。假设重启软件的时间为5分钟,修复硬件的时间为1小时。
#### 答案
我们可以通过计算每个组件故障导致的停机时间来估算服务的可用性。我们将保守地选取图6.1中每个类别的最低值,并将1000次停机均匀分配到四个组件之间。我们忽略慢速磁盘——1000次停机中的第五个组件——因为它们影响性能但不影响可用性,同时也忽略电力公用事业故障,因为不间断电源(UPS)系统隐藏了99%的故障。

没有软件冗余来掩盖众多故障,这2400台服务器上的服务平均每周会停机一天——零个“九”,这远低于WSCs的目标可用性99.99%。
根据第6.10节的解释,Web搜索计算中心(WSCs)的前身是计算机集群。集群是通过局域网(LAN)和交换机连接在一起的独立计算机集合。在不需要大量通信的工作负载中,集群提供了比共享内存多处理器更具成本效益的计算能力。(共享内存多处理器是第5章讨论的多核计算机的前身。)在1990年代末,集群在科学计算中变得流行,随后也用于互联网服务。对于WSCs的一种看法是,它们只是从数百台服务器的集群逻辑演变到数万台服务器的结果。
一个自然的问题是,WSCs是否与现代高性能计算(HPC)集群相似。尽管某些HPC设计在规模和成本上相似——有些设计拥有百万个处理器,花费数亿美元——但从历史上看,它们的处理器更强大,并且节点之间的网络延迟远低于WSCs,这是因为HPC应用程序的相互依赖性更强,并且通信更加频繁(见第6.3节)。编程环境还强调线程级并行或数据级并行(见第4章和第5章),通常关注完成单个任务的延迟,而不是通过请求级并行完成多个独立任务的带宽。此外,HPC集群通常会有长时间运行的作业,使得服务器能够充分利用,即使长达数周之久。WSC(Web搜索计算中心)中的服务器利用率通常在10%到50%之间(见第441页的图6.3),并且每天都在变化。与超级计算机环境不同,数千名开发人员共同维护WSC的代码库,并每周部署重要的软件版本(Barroso等,2017)。
那么,WSC与传统数据中心相比如何呢?传统数据中心的运营者一般会从组织的多个部分收集机器和第三方软件,并为其他人集中运行这些资源。他们的主要关注点往往是将许多服务整合到更少的机器上,这些机器彼此隔离以保护敏感信息。因此,虚拟机在数据中心中变得越来越重要。虚拟机对WSC同样重要,但它们扮演着不同的角色。虚拟机用于在不同客户之间提供隔离,并将硬件资源划分为不同大小的份额,以便以多种价格点进行租赁(见第6.5节)。与WSC不同,传统数据中心通常具有大量的硬件和软件异构性,以服务于组织内部不同客户的需求。WSC程序员会自定义第三方软件或构建自己的软件,而WSC则拥有更为同质的硬件;WSC的目标是使仓库中的硬件/软件像一台单一计算机那样运行多种应用程序。通常,传统数据中心最大的成本是维护这些设施的人力,而正如我们将在第6.4节中看到的,在设计良好的WSC中,服务器硬件是最大的成本,人力成本则从最高层转移到最低层。传统数据中心的规模也无法与WSC相比,因此无法获得前面提到的规模经济效益。
因此,尽管WSC可以被视为一种极端的数据中心,因为计算机在具有特殊电气和冷却基础设施的空间中单独安置,但在架构和运营上,传统数据中心与WSC面临的挑战和机遇几乎没有共同之处。
我们将从WSC的工作负载和编程模型开始介绍。
6.2 Programming Models and Workloads for Warehouse-Scale Computers
如果一个问题没有解决方案,它可能不是一个问题,而是一个事实——不是要被解决,而是要随着时间来应对。
—— 西蒙·佩雷斯
除了公共互联网服务,如搜索、视频分享和社交网络,这些服务使它们声名显赫,WSC(Web搜索计算中心)还运行批处理应用程序,例如将视频转换为新格式或从网页抓取中创建搜索索引。
在WSC中,一个流行的批处理框架是MapReduce(Dean 和 Ghemawat, 2008)及其开源版本Hadoop。图6.2显示了MapReduce在谷歌上的日益普及。MapReduce受到同名Lisp函数的启发,Map首先将程序员提供的函数应用于每个逻辑输入记录。Map在数百台计算机上运行,以生成键值对的中间结果。Reduce收集这些分布式任务的输出,并使用另一个程序员定义的函数将其合并。假设Reduce函数是可交换和结合的,它可以在对数时间内运行。借助适当的软件支持,这两个函数既快速又易于理解和使用。在30分钟内,一位初学者就可以在数千台计算机上运行MapReduce任务。

图6.2 显示了2004年至2016年间谷歌的每月MapReduce使用情况。在12年中,MapReduce作业的数量增加了3300倍。页面461的图6.17估计,在亚马逊的云计算服务EC2上运行2016年9月的工作负载将耗费1.14亿美元。更新自Dean, J., 2009年。构建大型分布式系统的设计、经验和建议 [主题演讲]。收录于:第三届ACM SIGOPS国际大型分布式系统与中间件研讨会论文集,与第22届ACM操作系统原理研讨会共同举办,2009年10月11-14日,蒙大拿州大天空。
图6.2显示了平均作业使用了数百台服务器。除了少数来自高性能计算的高度优化应用程序外,这类MapReduce作业是目前最并行的应用,无论是按总CPU时间还是所使用的服务器数量进行测量。
以下是一个MapReduce程序,它计算在一大堆文档中每个英语单词的出现次数。下面是该程序的简化版本,仅显示内部循环,并假设文档中仅找到一个英语单词的出现(Dean 和 Ghemawat, 2008):

在Map函数中使用的EmitIntermediate函数会将文档中的每个单词及其值“1”发出。然后,Reduce函数使用ParseInt()对每个文档中每个单词的所有值进行求和,以获取在所有文档中每个单词的出现次数。MapReduce运行时环境将映射任务和归约任务调度到WSC的节点上。(程序的完整版本见Dean和Ghemawat(2008))。
MapReduce可以被视为单指令流、多数据流(SIMD)操作的推广(第4章),不同之处在于要应用的函数会传递给数据,随后是一个用于减少Map任务输出的函数。由于在SIMD程序中归约操作非常常见,SIMD硬件通常提供特殊的归约操作。例如,英特尔的AVX SIMD指令包括“水平”指令,用于加法操作,处理寄存器中相邻的操作数对。
为了适应数百台计算机性能的变异性,MapReduce调度器根据节点完成先前任务的速度分配新任务。显然,单个缓慢的任务可能会拖延大型MapReduce作业的完成。Dean和Barroso(2013)将这种情况称为尾延迟。在WSC中,解决慢任务的方法是提供软件机制来应对这种在该规模下固有的变异性。这一方法与传统数据中心中服务器的解决方案形成鲜明对比,后者通常认为慢任务意味着硬件故障需要更换,或者服务器软件需要调整和重写。在WSC中,50000至100000台服务器的性能异质性是常态。例如,在MapReduce程序的末尾,系统会开始在其他节点上备份尚未完成的任务,并从完成最快的任务中获取结果。通过增加资源使用率几个百分点,Dean和Ghemawat(2008)发现一些大型任务的完成速度提高了30%。
可靠性从一开始就被纳入MapReduce的设计中。例如,MapReduce作业中的每个节点都需要定期向主节点报告已完成任务的列表和更新状态。如果某个节点未在截止时间前报告,主节点会认为该节点已死,并将该节点的工作重新分配给其他节点。鉴于WSC中的设备数量,故障的普遍存在并不令人惊讶,正如之前的示例所示。为了实现99.99%的可用性,系统软件必须在WSC中应对这一现实。为了降低运营成本,所有WSC都使用自动监控软件,使一个操作员能够负责超过1000台服务器。
编程框架,如用于批处理的MapReduce和面向外部的SaaS(软件即服务)如搜索,引依赖于内部软件服务以实现成功。例如,MapReduce依赖于Google文件系统(GFS)(Ghemawat等,2003)或Colossus(Fikes,2010)来为任何计算机提供文件,以便在任何地方调度MapReduce任务。
除了GFS和Colossus,这些可扩展存储系统的例子还包括亚马逊的键值存储系统Dynamo(DeCandia等,2007)和谷歌的记录存储系统BigTable(Chang等,2006)。注意,这些系统通常是建立在彼此之上的。例如,BigTable将其日志和数据存储在GFS或Colossus上,就像关系数据库可能使用内核操作系统提供的文件系统一样。
这些内部服务通常做出与单服务器上运行的类似软件不同的决策。例如,与假设存储是可靠的(例如使用RAID存储服务器)不同,这些系统通常会创建数据的完整副本。副本可以帮助提高读取性能和可用性;通过适当的放置,副本能够克服许多其他系统故障,如图6.1所示。像Colossus这样的系统使用纠错码而不是完整副本来降低存储成本,但其不变的是跨服务器的冗余,而不是服务器内部或存储阵列内部的冗余。因此,整个服务器或存储设备的故障不会对数据的可用性产生负面影响。
另一种不同的方法是WSC存储软件通常使用放宽的一致性,而不是遵循传统数据库系统的所有ACID(原子性、一致性、隔离性和持久性)要求。其洞察在于,多个数据副本在某个时刻一致很重要,但对大多数应用而言,它们并不需要始终保持一致。例如,对于视频共享来说,最终一致性是可以接受的。最终一致性使存储系统更易于扩展,这是WSC的绝对要求。
这些公共交互服务的工作负载需求各不相同;甚至像谷歌搜索这样显著的全球服务,其需求随时间变化可达到两倍的差异。在考虑周末、假期以及某些应用(例如元旦后的照片共享服务或圣诞节前的在线购物)的热门时段时,服务器利用率的变化更加明显。图6.3显示了5000台谷歌服务器在6个月期间的平均利用率。请注意,少于0.5%的服务器平均达到100%利用率,大多数服务器的利用率在10%到50%之间。换句话说,仅有10%的服务器利用率超过50%。因此,在WSC中,服务器在低负载时表现良好比在高峰时高效更为重要,因为它们很少处于高峰状态。

图6.3 显示了在谷歌超过5000台服务器在6个月期间的平均CPU利用率。这些服务器很少完全闲置或完全利用,而是在大多数时间内以其最大利用率的10%到50%之间运行。图6.4中右侧的第三列计算了上下浮动5%的百分比来得出加权,因此90%这一行的1.2%意味着有1.2%的服务器的利用率在85%到95%之间。数据来源于Barroso, L.A., Holzle, U., 2007年的文章《能量比例计算的案例》。IEEE Computing. 40 (12), 33–37。
总之,WSC的硬件和软件必须应对基于用户需求的负载变化以及由于硬件在这种规模下的不确定性而导致的性能和可靠性变化。
示例:根据图6.3中的测量结果,SPECpower基准测试从0%负载到100%以10%的增量测量功耗和性能(见第1章)。该基准测试的总体单一指标是所有性能测量(每秒服务器端Java操作)的总和除以所有功耗测量(以瓦特为单位)的总和。因此,假设每个级别的可能性是相等的。如果根据图6.3中的利用率频率对这些级别进行加权,这些数值摘要指标将如何变化?
答案:图6.4显示了原始加权和与图6.3匹配的新加权。这些加权将性能摘要减少了30%,从3210 ssj_ops/watt降至2454。

考虑到规模,软件必须处理故障,这意味着几乎没有理由购买“镀金”的硬件来减少故障频率。主要的影响将是成本上升。Barroso和Hölzle(2009)发现,在运行TPC-C数据库基准测试时,高端惠普共享内存多处理器与普通惠普服务器之间的性价比相差20倍。不出所料,谷歌和所有其他拥有WSC(Web Server Cluster)公司的选择都是低端普通服务器。事实上,开放计算项目(http://opencompute.org)是一个这样的组织,致力于让这些公司共同合作,设计开放式的数据中心服务器和机架。
这些WSC服务通常倾向于开发自己的软件,而不是购买第三方商业软件,部分原因是为了应对巨大的规模,部分原因是为了节省成本。例如,即使在2017年TPC-C的最佳性价比平台上,添加SAP SQL Anywhere数据库和Windows操作系统的成本也会使Dell PowerEdge T620服务器的费用增加40%。相比之下,谷歌在其服务器上运行BigTable和Linux操作系统,因此无需支付许可费用。
在回顾了WSC的应用和系统软件之后,我们准备来看看WSC的计算机架构。
6.3 Computer Architecture of Warehouse-Scale Computers
网络是将50,000到100,000台服务器连接在一起的“连接组织”。类似于第二章中提到的内存层次结构,WSC(Web Server Cluster)使用了一种网络层次结构。图6.5展示了一个例子。理想情况下,综合网络能够提供接近为10万台服务器定制的高端交换机的性能,而成本则大约相当于为50台服务器设计的商品交换机的每个端口费用。正如我们将在第6.6节中看到的,WSC的网络是一个活跃创新的领域。

支撑服务器的结构是机架。尽管不同WSC的机架宽度各不相同——有些是经典的19英寸宽机架;其他的可能宽出两到三倍——但是高度通常不超过6到7英尺,因为需要有人来维护它们。这样的机架大约可以容纳40到80台服务器。由于在机架顶部连接网络电缆通常更为方便,因此这个交换机通常被称为机架顶端交换机(Top of Rack, ToR)。 (一些WSC有多个ToR交换机的机架。)
通常,机架内部的带宽远高于机架之间的带宽,因此如果发送者和接收者位于同一机架中,软件放置它们的位置就不那么重要。这种灵活性从软件的角度来看是理想的。这些交换机通常提供4到16个上行链路,连接到网络层次结构中的下一个更高的交换机。因此,离开机架的带宽是机架内部带宽的6到24倍更小。这个比率被称为超额订阅(oversubscription)。然而,大量的超额订阅意味着程序员在将发送者和接收者放置在不同机架时必须意识到性能的影响。这种增加的软件调度负担是专门为数据中心设计的网络交换机的另一个理由。
连接一系列机架的交换机相较于机架顶端交换机(ToR)要昂贵得多。这部分成本是由于更高的连接性,以及为了减少超额订阅问题,交换机的带宽必须大得多。Barroso等(2013)报告称,具有机架交换机10倍分割带宽——即基本上是最坏情况下的内部带宽——的交换机成本约为机架交换机的100倍。原因之一是交换机的带宽成本随着端口数量n的增加而增长,呈现n²的关系。第6.6节和第6.7节将详细描述ToR交换机以上的网络结构。
存储
一种自然的设计是填满机架中的服务器,除去为交换机保留的空间。这种设计留下了存储放置位置的问题。从硬件构建的角度来看,最简单的解决方案是在机架内包含磁盘,并依靠以太网连接来访问远程服务器上的信息。一个昂贵的替代方案是使用网络附加存储(NAS),可能通过如InfiniBand这样的存储网络。在过去,WSC通常依赖本地磁盘,并提供处理连接性和可靠性的存储软件。例如,GFS使用本地磁盘并维护副本,以克服可靠性问题。这种冗余不仅覆盖了本地磁盘故障,还包括对机架和整个集群的电源故障。GFS的最终一致性灵活性降低了保持副本一致性的成本,也减少了存储系统的网络带宽需求。
如今的存储选项更加多样化。尽管一些机架在服务器和磁盘之间达到了平衡,如同过去一样,也可能存在没有本地磁盘的机架,以及装满磁盘的机架。现代系统软件通常使用类似RAID的错误修正编码来降低可靠性的存储成本。
需要注意的是,在讨论WSC架构时,“集群”一词常常会引起混淆。根据第6.1节的定义,WSC只是一个极大的集群。相反,Barroso等(2013)使用“集群”一词来指代下一个规模的计算机组合,包含许多机架。在本章中,为了避免混淆,我们将使用“阵列”一词来表示以行组织的大型机架集合,同时保留“集群”一词的原始定义,用以表示从机架内联网计算机的集合到充满联网计算机的整个仓库。
### WSC 内存层次结构
图 6.6 显示了 WSC 内部内存层次结构的延迟、带宽和容量,图 6.7 则以视觉方式展示了相同的数据。这些图基于以下假设(Barroso 等,2013):
- 每台服务器包含 16 GiB 的内存,访问时间为 100 纳秒,传输速率为 20 GB/s;128 GiB 的闪存,延迟为 100 微秒,传输速率为 1 GB/s;以及 2 TB 的磁盘,访问时间为 10 毫秒,传输速率为 200 MB/s。每块板上有两个插槽,它们共享一个 1 Gbit/s 的以太网端口。
- 在这个例子中,每对机架包括一个机架交换机,并包含 80 台服务器。网络软件加上交换机的开销将 DRAM 的延迟增加到 100 微秒,将磁盘访问延迟增加到 11 毫秒。因此,一个机架的总存储容量大约为 1 TB 的 DRAM、20 TB 的闪存和 160 TB 的磁盘存储。1 Gbit/s 的以太网限制了机架内远程访问 DRAM、闪存或磁盘的带宽为 100 MB/s。
- 阵列由 30 个机架组成,因此阵列的存储容量增长了 30 倍:30 TB 的 DRAM、600 TB 的闪存和 4.8 PB 的磁盘。阵列交换机的硬件和软件将阵列内部 DRAM 的延迟增加到 500 微秒,闪存为 600 微秒,磁盘延迟为 12 毫秒。阵列交换机的带宽限制了远程访问阵列 DRAM、阵列闪存或阵列磁盘的带宽为 10 MB/s。


图 6.6 和 6.7 显示,网络开销显著增加了局部 DRAM 和闪存、机架 DRAM 和闪存、或阵列 DRAM 和闪存之间的延迟,但所有这些仍然比访问本地磁盘的延迟好 10 倍以上。网络缩小了机架 DRAM、闪存与磁盘之间,以及阵列 DRAM、闪存与磁盘之间的带宽差异。

图 6.8 显示了用于将阵列连接在一起并与互联网连接的第 3 层网络(Greenberg 等,2009)。负载均衡器监控一组服务器的繁忙程度,并将流量引导到负载较轻的服务器,以尽量保持服务器的利用率大致相同。另一种选择是使用单独的边界路由器将互联网连接到数据中心的第 3 层交换机。如我们将在第 6.6 节看到的,许多现代 WSC 已经放弃了传统交换机的常规分层网络架构。
WSC 需要 40 个阵列才能达到 100,000 台服务器,因此网络层次结构中还有一个层级。图 6.8 显示了常规的三层路由器,用于将阵列连接在一起并与互联网连接。
大多数应用程序适合在 WSC 中的单个阵列内运行。那些需要多个阵列的应用采用分片或分区的方法,这意味着数据集被拆分为独立的部分,然后分发到不同的阵列中。可以将其类比为注册会议时,由一个人处理 A 到 M 的名字,另一个人处理 N 到 Z。对整个数据集的操作被发送到托管这些部分的服务器,结果由客户端计算机汇总。
**示例**:假设 90% 的访问是本地于服务器,9% 是在机架内但不在服务器内,1% 是在机架外但仍在阵列内,平均内存延迟是多少?
**答案**:平均内存访问时间为:
![]()
这比 100% 本地访问的情况慢了超过 300 倍。显然,服务器内的访问局部性对 WSC 性能至关重要。
**示例**:在服务器内部、机架内的服务器之间,以及阵列中不同机架之间,传输 1000 MB 的数据需要多长时间?在这三种情况下,DRAM 之间传输 1000 MB 的速度又快多少?

因此,对于单个服务器外的块传输,数据是存储在内存中还是在磁盘上并不重要,因为机架交换机和阵列交换机是瓶颈。这些性能限制影响了 WSC 软件的设计,并促使对更高性能交换机的需求(见第 6.6 节)。
虽然这些例子具有教育意义,但请注意,计算机和网络设备的规模和速度可能远大于这些来自 2013 年的示例(见第 6.7 节)。到 2017 年,服务器的 DRAM 部署已达到 256–1024 GiB,而最近的交换机将延迟降低到了每跳仅 300 纳秒。考虑到 IT 设备的架构,我们现在可以讨论如何为其布置、供电和降温,以及与仅仅在其中的 IT 设备相比,构建和运营整个 WSC 的成本。
6.4 The Efficiency and Cost of Warehouse-Scale Computers
基础设施成本,包括电力分配和冷却,是 WSC 建设成本的主要部分,因此我们将重点讨论这些内容。(第 6.7 节详细描述了 WSC 的电力和冷却基础设施。)
计算机房空调(CRAC)单元通过冷却水来冷却服务器房间的空气,类似于冰箱通过将热量释放到外部来去除热量。当液体吸收热量时,它就会蒸发。相反,当液体释放热量时,它会凝结。空调将液体泵送到低压线圈中以蒸发并吸收热量,然后将其送到外部冷凝器进行释放。因此,在 CRAC 单元中,风扇将温暖的空气推过一组充满冷水的线圈,泵则将加热后的水输送到制冷设备进行冷却。图 6.9 显示了大量的风扇和水泵,它们在系统中移动空气和水。

图 6.9 冷却系统的机械设计。CWS 代表循环水系统。来源于 Hamilton, J., 2010。云计算的规模经济。在:2010 年 6 月 8 日在华盛顿州西雅图举办的 AWS 基因组学与云计算研讨会上提交的论文。http://mvdirona.com/jrh/TalksAndPapers/JamesHamilton_GenomicsCloud20100608.pdf。
除了制冷设备,一些数据中心利用较冷的外部空气或水温在水送往制冷设备之前进行预冷。然而,根据地点的不同,在一年中的温暖时期,制冷设备仍可能是必需的。令人惊讶的是,如何在考虑电力分配和冷却的开销后,计算一个 WSC 能支持多少台服务器并不明显。来自服务器制造商的额定功率总是比较保守:这是服务器能够消耗的最大功率。因此,第一步是在多种工作负载下测量单个服务器的功率,以便在 WSC 中部署。(网络通常占总功耗的约 5%,因此在初期可以忽略。)
为了确定 WSC 的服务器数量,可将可用于 IT 设备的电力除以测得的单台服务器功率;然而,根据 Fan 等人的研究(2007),这仍然过于保守。他们发现,在理论上,成千上万台服务器在最坏情况下可以做到的与它们在实际中会做到的之间存在显著差距,因为没有真实的工作负载会使成千上万台服务器同时达到其峰值。他们发现,可以根据单台服务器的功率安全地超额订阅多达 40% 的服务器数量。他们建议 WSC 设计师这样做,以提高 WSC 内电力的平均利用率;但他们也建议使用广泛的监控软件以及安全机制,在工作负载变化时取消调度低优先级任务。
以下是 2012 年在 Google WSC 中的 IT 设备内部的电力使用情况(Barroso et al., 2013):

测量 WSC 的效率
评估数据中心或 WSC 效率的一种广泛使用的简单指标称为电力利用效率(PUE):
![]()
因此,PUE 必须大于或等于 1,PUE 越大,WSC 的效率越低。
Greenberg 等人(2009)对 19 个数据中心的 PUE 和用于冷却基础设施的开销部分进行了报告。图 6.10 显示了他们的发现,按 PUE 从高到低排序。中位数 PUE 为 1.69,冷却基础设施使用的电力超过服务器的一半——平均而言,1.69 中有 0.55 是用于冷却。需要注意的是,这些是平均 PUE,可能会因工作负载甚至外部空气温度的变化而每天有所不同,如我们将看到的(图 6.11)。

图 6.10 2006 年 19 个数据中心的电力利用效率(Greenberg 等,2009)。在计算 PUE 时,空调(AC)和其他用途(如电力分配)的电力被归一化为 IT 设备的电力。因此,IT 设备的电力必须为 1.0,而空调的电力大约在 IT 设备电力的 0.30 到 1.40 倍之间。“其他”用途的电力大约占 IT 设备的 0.05 到 0.60 倍。

图 6.11 2008 年至 2017 年间 15 个 Google WSC 的平均电力利用效率(PUE)。波动的线条表示季度平均 PUE,而相对平直的线条表示过去 12 个月的平均 PUE。对于 2016 年第四季度,平均值分别为 1.11 和 1.12。
在过去十年中,随着对 PUE 的关注,数据中心的效率大大提高。然而,正如第 6.8 节所解释的,关于 PUE 的包含内容尚无普遍接受的定义:如果用于在停电时保持运行的电池位于单独的建筑中,它们是否应被计算在内?你是从电力变电站的输出测量,还是从电力首次进入 WSC 的地方测量?图 6.10 显示了所有 Google 数据中心平均 PUE 的随时间变化的改善,而这个 PUE 是 Google 综合测量的。
由于每美元的性能是最终指标,我们仍然需要测量性能。如图 6.7 所示,带宽下降和延迟增加取决于与数据的距离。在 WSC 中,服务器内的 DRAM 带宽是机架内的 200 倍,而机架内又是阵列内的 10 倍。因此,在 WSC 内数据和程序的放置上,还有另一种局部性需要考虑。
尽管 WSC 的设计师通常关注带宽,但在 WSC 上开发应用程序的程序员也关心延迟,因为延迟是用户可以感知到的。用户的满意度和生产力与服务的响应时间密切相关。一些关于时间共享时代的研究报告显示,用户生产力与交互时间成反比,交互时间通常分为人机输入时间、系统响应时间以及用户在按下下一个输入前思考响应所需的时间(Doherty 和 Thadhani,1982)。实验结果表明,将系统响应时间缩短 30% 可以将交互时间缩短 70%(Brady,1986)。这一看似不合理的结果可以用人性来解释:当给出更快的响应时,人们思考所需的时间更少,因为他们不太可能分心,能够保持“状态”。

图 6.12 显示了针对 Bing 搜索引擎的一项较新实验的结果,该实验在搜索服务器上插入了 50-2000 毫秒的延迟(Schurman 和 Brutlag,2009)。与之前的研究结果一致,下一次点击的时间大约翻倍了延迟;也就是说,服务器上的 200 毫秒延迟导致下一次点击时间增加 500 毫秒。随着延迟的增加,收入呈线性下降,用户满意度也随之降低。另一项关于 Google 搜索引擎的独立研究发现,这些影响在为期 4 周的实验结束后仍然持续。五周后,经历 200 毫秒延迟的用户每天的搜索人数减少了 0.1%,而经历 400 毫秒延迟的用户搜索次数则减少了 0.2%。考虑到搜索带来的巨大收益,即使是如此微小的变化也令人不安。事实上,结果如此消极,以至于他们提前结束了实验。
由于对互联网服务所有用户满意度的极度关注,性能目标通常会被设定为确保高比例的请求低于某个延迟阈值,而不仅仅是针对平均延迟设定目标。这样的阈值目标被称为服务水平目标(SLOs)。例如,一个 SLO 可能规定 99% 的请求必须低于 100 毫秒。因此,亚马逊的 Dynamo 键值存储系统的设计者决定,为了在 Dynamo 上提供良好的延迟,他们的存储系统必须在 99.9% 的时间内达到其延迟目标(DeCandia 等,2007)。例如,Dynamo 的一项改进帮助了 99.9 百分位的性能,远超过平均情况,这反映了他们的优先事项。
Dean 和 Barroso(2013)提出了“尾容忍”这一术语,用来描述旨在满足此类目标的系统:就像容错计算旨在将不太可靠的部分构建成一个可靠的整体,大型在线服务需要从不太可预测的部分中创建一个可预测的响应整体。
造成不可预测性的原因包括对共享资源(处理器、网络等)的争用、排队、由于 Turbo 模式或动态电压频率调整(DVFS)等优化引起的微处理器性能波动、软件垃圾回收等等。谷歌得出的结论是,与其试图防止 WSC 中的这种可变性,不如开发尾容忍技术来掩盖或绕过临时的延迟峰值。例如,细粒度负载均衡可以快速在服务器之间移动小量工作,从而减少排队延迟。
### WSC的成本
如引言中所提到的,与大多数架构师不同,WSC的设计者不仅关注建造WSC的成本,还关注其运营成本。会计将前者称为运营支出(OPEX),将后者称为资本支出(CAPEX)。
为了更好地理解能源成本,Hamilton(2010)进行了一项案例研究以估算WSC的成本。他确定一个8兆瓦(MW)设施的CAPEX为8800万美元,约46000台服务器和相应的网络设备又增加了7900万美元的CAPEX。图6.13显示了该案例研究的其他假设。

图6.13 WSC案例研究,四舍五入到最近的5000美元。互联网带宽成本因应用而异,因此未在此包含。设施的剩余18% CAPEX包括购买物业和建筑成本。我们在图6.14中添加了安全和设施管理的人力成本,这些并不在案例研究中。请注意,Hamilton的估算是在他加入亚马逊之前进行的,且并不是基于某个特定公司的WSC。参考文献:Hamilton, J., 2010. 云计算规模经济。发表于2010年6月8日,在华盛顿州西雅图举行的AWS基因组学与云计算研讨会上。http://mvdirona.com/jrh/TalksAndPapers/JamesHamilton_GenomicsCloud20100608.pdf。
Hamilton的研究结果显示建筑、电力和冷却的成本为每瓦11美元。Barroso等人(2013)在多个案例中报告了相似的结果,成本为每瓦9到13美元。因此,一个16 MW的设施成本为1.44亿到2.08亿美元,不包括计算、存储和网络设备的费用。
我们可以通过资本成本转换将CAPEX转换为OPEX,假设借款成本为5%,这是美国会计规则中的标准惯例。也就是说,我们可以将CAPEX作为固定金额在设备的有效使用寿命内每月摊销。图6.14详细列出了Hamilton案例研究的每月OPEX。请注意,他的案例研究中,摊销率差异显著:设施为10年,网络设备为4年,服务器为3年。因此,WSC设施的使用寿命为十年,但服务器每3年更换一次,网络设备每4年更换一次。通过摊销CAPEX,Hamilton得出了每月OPEX,包括借款成本(年利率5%)来支付WSC的费用。每月OPEX为380万美元,约占CAPEX的2%(或年均24%)。
这个数据为我们提供了一个实用的指导原则,以便在关注能源时做出关于使用哪些组件的决策。在WSC中,每年的完全负担成本(包括电力和冷却基础设施的摊销成本)为每瓦...
![]()
成本大约为每瓦每年2美元。因此,通过节能降低成本不应导致支出超过每瓦每年2美元(见第6.8节)。注意,在图6.14中,超过三分之一的运营支出(OPEX)与电力相关,该类别的支出呈上升趋势,而服务器成本则在下降。网络设备占总OPEX的8%和服务器CAPEX的19%,而且网络设备的成本下降速度不如服务器快,这可能是由于对更高网络带宽的持续需求(见第467页的图6.22)。这种差异在机架上方的网络层次结构中的交换机尤其明显,它们代表了大部分网络成本(见第6.6节)。安全和设施管理的人力成本仅占OPEX的2%。将图6.14中的OPEX除以服务器数量和每月的小时数,成本约为每台服务器每小时0.11美元。

图6.14 图6.13的每月运营支出(OPEX),四舍五入到最近的5000美元。注意,服务器的3年摊销意味着每3年购买新的服务器,而设施的摊销期为10年。因此,服务器的摊销资本成本约为设施的三倍。人力成本包括三名保安职位,全天24小时、全年365天,每人每小时20美元,以及一名设施管理人员,全天24小时、全年365天,每小时30美元。员工福利为薪资的30%。此计算未包括互联网的网络带宽费用,因为它因应用而异,也未包括供应商维护费用,因为这些费用因设备和谈判而异。
Barroso等(2013)评估了资本支出(CAPEX)和运营支出(OPEX),以每瓦每月的成本进行计算。因此,如果一个12兆瓦(MW)的风能发电系统(WSC)在12年内折旧,折旧成本为每瓦每月0.08美元。他们假设公司通过申请年利率8%的贷款获得WSC的资金——企业贷款通常在7%到12%之间——而利息支付又增加了0.05美元,总计为每瓦每月0.13美元。他们对服务器的成本进行了类似的计算。一台500瓦的服务器成本为4000美元,折合每瓦8美元,4年的折旧为每瓦每月0.17美元。服务器贷款的8%利息又增加了0.02美元。他们估算网络成本为每瓦每月0.03美元。他们报告称,多兆瓦WSC的典型OPEX成本在每瓦每月0.02到0.08美元之间。总成本为每瓦每月0.37到0.43美元。对于一个8兆瓦的WSC,扣除电力成本后的月支出约为300万到350万美元。如果我们从汉密尔顿的计算中减去每月的电力使用,他的月度估算将为330万美元。考虑到预测成本的不同方法,这些估算结果相当一致。
示例:美国各地区电力成本从每千瓦时0.03美元到0.15美元不等。这两种极端价格对每小时服务器成本的影响是什么?
答案:我们将8兆瓦的关键负载乘以图6.13(第二行)的平均PUE,以计算平均功耗:
![]()
电力的月成本因此从图6.14中的475,000美元下降到在每千瓦时0.03美元时的205,000美元,以及在每千瓦时0.15美元时的1,015,000美元。这些电力成本的变化使得每小时的服务器成本分别变为0.11美元、0.10美元和0.13美元。
示例:如果所有折旧时间都统一为5年,月成本会发生什么变化?这将如何改变每台服务器的小时成本?
答案:电子表格可在线访问:http://mvdirona.com/jrh/TalksAndPapers/PerspectivesDataCenterCostAndPower.xls。将折旧时间改为5年后,图6.14的前四行将变为:

总的月运营支出为3,422,000美元。如果我们每5年更换一次所有设备,成本将为每台服务器每小时0.103美元,此时折旧成本更多地用于设施而非服务器,如图6.14所示。
每台服务器每小时约0.10美元的费用相比于许多拥有和运营自己(较小)传统数据中心的公司来说要低得多。WSC的成本优势促使大型互联网公司提供计算服务作为一种公用事业,用户只需为实际使用的部分付费,类似于电力支付。如今,公用计算更广为人知的是云计算。
6.5 Cloud Computing: The Return of Utility Computing
如果我所倡导的计算机成为未来的计算机,那么计算可能会像电话系统一样被组织为公用事业……计算公用事业可能成为一个新兴且重要产业的基础。
——约翰·麦卡锡,麻省理工学院百年庆典(1961年)
随着用户数量不断增加,互联网公司如亚马逊、谷歌和微软从普通组件中构建了越来越大的仓库级计算机,这使得麦卡锡的预测最终成真,但并不是因为他所想的时分共享的普及。这样的需求推动了系统软件的创新,以支持在这一规模下的操作,包括BigTable、Colossus、Dynamo、GFS和MapReduce。同时,这也要求改进运营技术,以确保服务在至少99.99%的时间内可用,即使面对组件故障和安全攻击。这些技术的例子包括故障转移、防火墙、虚拟机以及对分布式拒绝服务攻击的保护。随着软件和专业知识提供了可扩展性,并且客户需求不断增加以证明投资的合理性,拥有50,000到100,000台服务器的WSC在2017年变得司空见惯。
随着规模的扩大,经济效益也随之增加。根据2006年的一项研究,比较了一个WSC与仅有1000台服务器的数据中心,汉密尔顿(2010年)报告了以下优势:
- 存储成本减少5.7倍——WSC的磁盘存储成本为每GB每年4.6美元,而数据中心则为每GB 26美元。
- 行政成本减少7.1倍——WSC的每位管理员所管理的服务器比例超过1000,而数据中心仅为140。
- 网络成本减少7.3倍——WSC的互联网带宽每Mbit/s每月成本为13美元,而数据中心为95美元。不出所料,订购1000 Mbit/s的价格远比订购10 Mbit/s要好得多。
另一个规模经济的来源是在采购过程中。高水平的采购导致WSC几乎所有物品都享有批量折扣价格。
规模经济同样适用于运营成本。从前面的部分我们看到,许多数据中心的PUE(电力使用效率)为2.0。大型企业可以合理地雇佣机械和电力工程师开发PUE在1.1到1.2范围内的WSC(大规模网络数据中心)(见第6.7节)。
互联网服务需要分布到多个WSC,以提高可靠性并减少延迟,尤其是在国际市场上。因此,所有大型企业都出于这个原因使用多个WSC。对于个体公司来说,在全球范围内建立多个小型数据中心的成本远高于在其总部建立一个大型数据中心。
最后,基于第6.1节提出的原因,数据中心的服务器通常仅被利用10%到20%的时间。通过向公众开放WSC,不同客户之间的非相关峰值可以将平均利用率提高到50%以上。
因此,WSC的规模经济为WSC的多个组件提供了5到7倍的效益,而整个WSC则有1.5到2倍的效益。
自本书最后一版以来,对云计算安全性的关注发生了转变。2011年,人们对将关键数据放入云中表示怀疑,因为这可能使黑客更容易入侵,而如果数据保存在本地数据中心(“on prem”)中则更加安全。到2017年,数据中心的入侵事件变得如此普遍,以至于几乎不再上新闻。例如,这种不安全感甚至导致了勒索软件的快速增长——犯罪分子入侵、加密组织的所有数据,直到支付赎金才释放密钥——2015年给企业造成了10亿美元的损失。相比之下,WSC始终处于攻击之下,其运营商能够更快速地做出反应以阻止这些攻击,从而建立更好的防御。因此,WSC内部几乎没有勒索软件的出现。WSC显然比今天绝大多数本地数据中心更安全,因此许多首席信息官现在认为,关键数据在云中比“on prem”更安全。
虽然有多个云计算提供商,但我们重点介绍亚马逊网络服务(AWS),因为它是最古老且目前最大的商业云服务提供商之一。
### 亚马逊网络服务
公共计算的概念可以追溯到商业时分系统,甚至是1960年代和1970年代的批处理系统,当时公司只需支付终端和电话线费用,然后根据使用的计算量进行计费。自时分系统结束以来,许多努力尝试提供这样的按需服务,但往往以失败告终。
当亚马逊在2006年通过亚马逊简单存储服务(Amazon S3)和亚马逊弹性计算云(Amazon EC2)开始提供公共计算时,做出了一些新颖的技术和商业决策:
- **虚拟机**:使用运行Linux操作系统的x86商品计算机和Xen虚拟机构建WSC解决了几个问题。首先,它允许亚马逊保护用户之间的隔离。其次,它简化了WSC内部的软件分发,客户只需安装一个映像,AWS会自动将其分发到所有正在使用的实例。第三,可靠地终止虚拟机使亚马逊和客户能够轻松控制资源使用。第四,虚拟机可以限制物理处理器、磁盘和网络的使用速率,以及主内存的数量,这为AWS提供了多个价格选项:通过在单个服务器上打包多个虚拟核心提供最低价格选项,提供对所有机器资源的独占访问作为最高价格选项,以及多个中间价位。第五,虚拟机隐藏了硬件的身份,使AWS能够继续销售较旧机器的使用时间,如果客户知道机器的年龄,则这些机器可能不再具有吸引力。最后,虚拟机允许AWS通过在每台服务器上打包更多虚拟核心或提供每个虚拟核心性能更高的实例来引入新的更快硬件;虚拟化意味着所提供的性能不必是硬件性能的整数倍。
- **非常低的成本**:当AWS在2006年宣布每小时每实例$0.10的费率时,这是一个令人震惊的低金额。一个实例即一个虚拟机,以$0.10每小时的价格,AWS在多核服务器上为每个核心分配两个实例。因此,一个EC2计算单元相当于当时1.0–1.2 GHz的AMD Opteron或Intel Xeon。
- **(初始)依赖开源软件**:良好质量的软件可用,无需许可问题或在数百或数千台服务器上运行的费用,使得公共计算对亚马逊及其客户都变得更加经济划算。AWS后来开始以更高的价格提供包括商业第三方软件的实例。
- **无(初始)服务保证**:亚马逊最初仅承诺尽力而为。低成本如此有吸引力,以至于许多人可以在没有服务保证的情况下生存。如今,AWS在如Amazon EC2和Amazon S3等服务上提供高达99.95%的可用性服务水平目标。此外,Amazon S3旨在通过在多个位置保存每个对象的多个副本来实现耐用性。(根据AWS的数据,永久丢失对象的几率为1000亿分之一。)AWS还提供服务健康仪表板,实时显示每项AWS服务的当前操作状态,确保AWS的正常运行时间和性能完全透明。
- **无需合同**:部分原因是成本如此低,开始使用EC2所需的仅是一张信用卡。

图6.15显示了2017年2月美国弗吉尼亚地区按需通用和计算优化EC2实例的价格和特征。当AWS刚开始时,一个EC2计算单元相当于2006年的1.0–1.2 GHz AMD Opteron或Intel Xeon。可变实例是最新和最便宜的类别。如果您的工作负载在24小时内平均利用核心的不到5%,则可变实例可提供高频Intel CPU核心的全部性能,例如用于服务网页。AWS还提供Spot实例,费用低得多(约25%)。使用Spot实例时,客户可以设置愿意支付的价格和希望运行的实例数量,然后当现货价格低于他们的出价时,AWS会运行这些出价。AWS还提供保留实例,适用于那些客户知道自己将在一年内使用大部分实例的情况。客户需支付每个实例的年度费用,然后按小时费率使用服务,该费率约为第一列的30%。如果保留实例在整整一年内100%使用,则包括年费摊销在内的每小时平均成本将约为第一列费率的65%。EBS是弹性块存储,它是一种原始块级存储系统,存在于网络的其他地方,而不是在与虚拟机同一服务器内的本地磁盘或本地固态硬盘(SSD)中。

图6.15和图6.16显示了2017年多种类型EC2实例的每小时价格。从2006年的10种实例类型扩展到现在的50多种。最快的实例比最慢的快100倍,而最大的实例提供的内存是最小实例的2000倍。租用最便宜的实例整整一年的费用仅为50美元。
除了计算,EC2还对长期存储和互联网流量收费。(在AWS区域内的网络流量没有费用。)使用SSD时,弹性块存储(EBS)的费用为每GB每月0.10美元,而硬盘驱动器的费用为每GB每月0.045美元。互联网流量方面,从EC2传入的费用为每GB 0.01美元,从EC2传出的费用为每GB 0.09美元。
示例:计算在EC2上运行图6.2中平均MapReduce作业的成本,时间跨度为数年。假设有大量作业,因此没有显著的额外成本来四舍五入以获得整数小时数。接下来,计算每月运行所有MapReduce作业的费用。
答案:第一个问题是,什么样的实例大小最适合与Google的典型服务器匹配?我们假设图6.15中最接近的匹配是c4.large,具有2个虚拟核心和3.6 GiB内存,费用为每小时0.100美元。图6.17计算了在EC2上运行Google MapReduce工作负载的平均和总年度费用。2016年9月的平均MapReduce作业在EC2上的费用略高于1美元,而该月份的总工作负载在AWS上的费用为1.14亿美元。

示例:考虑到MapReduce作业的成本,假设你的老板希望你调查降低成本的方法。使用AWS Spot实例可能节省多少费用?
答案:MapReduce作业可能会因被驱逐出Spot实例而中断,但MapReduce被设计为能够容忍并重新启动失败的作业。c4.large的AWS Spot价格为0.0242美元,而标准价格为0.100美元,这意味着2016年9月节省了8700万美元,但没有对响应时间提供任何保证!
除了低成本和按需付费的公用计算模型,云计算用户的另一个强大吸引力在于云计算提供商承担了过度配置或不足配置的风险。因为这两种错误都可能致命,规避风险对初创公司来说是件好事。如果在产品尚未准备好大规模使用之前就花费过多宝贵的投资在服务器上,公司可能会耗尽资金。如果服务突然变得受欢迎,但没有足够的服务器来满足需求,公司可能会给潜在的新客户留下非常糟糕的印象,而这些客户正是公司成长所急需的。
这个场景的典型例子是Zynga的FarmVille,这是一款在Facebook上的社交网络游戏。在FarmVille宣布之前,最大的社交游戏每天约有五百万玩家。FarmVille在推出后4天内便拥有了一百万玩家,60天后达到了千万玩家。270天后,它每天有2800万玩家,每月有7500万玩家。由于FarmVille部署在AWS上,它能够随着用户数量的增长而无缝扩展。此外,它能够根据客户需求和时间段进行负载调整。
FarmVille如此成功,以至于Zynga在2012年决定开设自己的数据中心。但在2015年,Zynga又回到了AWS,认为让AWS管理其数据中心更为合适。当FarmVille在2016年从Facebook最受欢迎的应用程序下降到第110位时,Zynga能够与AWS优雅地缩减规模,就像它最初与AWS一起增长一样。
在2014年,AWS提供了一项新服务,唤起了约翰·麦卡锡在本节开头引用中提到的1960年代时间共享时期的回忆。Lambda允许用户提供源代码中的函数(如Python),并让AWS自动管理该代码所需的资源,以便根据输入大小进行扩展,并保持高可用性。Google Cloud Compute Functions和Microsoft Azure Functions是竞争云供应商提供的等效功能。如第6.10节所述,Google App Engine在2008年最初提供了非常类似的服务。
这一趋势被称为无服务器计算,用户无需管理服务器(但这些函数实际上是在服务器上运行)。提供的任务包括操作系统维护、容量配置和自动扩展、代码和安全补丁部署,以及代码监控和日志记录。它会根据事件(如HTTP请求或数据库更新)运行代码。可以将无服务器计算视为一组在整个WSC中并行运行的过程,通过如AWS S3这样的去聚合存储服务共享数据。
无服务器计算在程序空闲时是没有费用的。AWS 的计费精度比 EC2 高六个数量级,使用时间以 100 毫秒为单位记录,而不是按小时计费。费用根据所需内存的多少而有所不同,但如果你的程序使用 1 GiB 内存,则费用为每 100 毫秒 $0.000001667,约合每小时 $6。
无服务器计算可以被视为实现云计算理想的下一个进化步骤,它将数据中心视为一台计算机,采用按需付费的定价模式,并作为自动动态扩展的一种手段。
云计算使得 WSC 的好处对每个人都可用。云计算提供了成本关联性,给用户带来了无限扩展的幻觉,且无需额外费用:1000 台服务器运行 1 小时的成本与 1 台服务器运行 1000 小时相同。云计算提供商有责任确保有足够的服务器、存储和互联网带宽来满足需求。前面提到的优化供应链将新计算机的交付时间缩短到一周,这在提供这种幻觉时对供应商来说是一个重要的帮助,而不会导致破产。这种风险转移、成本关联性、按需付费定价和更高的安全性,是各类公司使用云计算的强大理由。
AWS 云有多大?
AWS 于 2006 年启动,发展迅速,以至于亚马逊(Amazon.com)在 2010 年成为 AWS 的客户,而不是使用单独的计算基础设施。图 6.18 显示,在 2017 年,AWS 在全球设有 16 个地点,并且还有两个正在建设中。作为一个有趣的补充,图 6.19 和 6.20 显示了 Google 和 Microsoft 的类似地图。

图 6.18 在 2017 年,AWS 有 16 个站点(“区域”),并且还有两个即将开放。大多数站点有两个到三个可用区,这些可用区位于附近,但不太可能受到同一自然灾害或电力故障的影响(如果发生的话)。(每个圆圈内列出了可用区的数量。)这 16 个站点或区域总共有 42 个可用区。每个可用区有一个或多个 WSC。https://aws.amazon.com/about-aws/global-infrastructure/。

图 6.19 在 2017 年,Google 有 15 个站点。在美洲:南卡罗来纳州伯克利县、爱荷华州委员会布拉夫斯、乔治亚州道格拉斯县、阿拉巴马州杰克逊县、北卡罗来纳州莱诺尔、俄克拉荷马州梅斯县、田纳西州蒙哥马利县、智利基利库拉和俄勒冈州达尔斯。在亚洲:台湾彰化县;新加坡。在欧洲:爱尔兰都柏林、荷兰埃姆斯哈芬、芬兰哈米纳和比利时圣吉斯兰。https://www.google.com/about/datacenters/inside/locations/。

每个 AWS 地点由两个到三个相邻的设施(相距一到两公里)组成,称为可用区(availability zones)。之所以这样命名,是因为在两个可用区上运行软件是安全的,这样可以确保可靠性,因为同时出现电力故障或自然灾害导致两者都失效的可能性较低(Hamilton, 2014)。这 16 个地点包含 42 个可用区,每个可用区有一个或多个 WSC。到 2014 年,每个 WSC 至少有 50,000 台服务器,有些甚至超过 80,000 台。
汉密尔顿(2017)指出,每个区域至少应有三个 WSC。原因很简单,当一个 WSC 发生故障时,区域内的其他 WSC 需要承担故障 WSC 的负载。如果只有一个其他 WSC,每个 WSC 都必须保留一半的容量用于故障转移。而有三个时,它们可以在三分之二的容量下使用,并且仍然能够快速进行故障转移。数据中心越多,预留的冗余容量就越少;AWS 有超过 10 个 WSC 的区域。
我们找到两项关于 2014 年 AWS 总服务器数量的公开估计。一项估计为 200 万台服务器,当时 AWS 只有 11 个区域和 28 个可用区(克拉克,2014)。另一项估计在 280 万到 560 万台服务器之间(摩根,2014)。如果我们根据可用区数量的增加,从 2014 年推算到 2017 年,估计将增长到低端 300 万台,高端 840 万台。WSC(数据中心)的总数为 84 到 126 个。图 6.21 显示了随着时间的推移,这两个预测的外推结果,提供了服务器和 WSC 数量的高低估计。
AWS 对实际数量保持沉默是可以理解的。他们表示,2014 年 AWS 拥有超过 100 万个客户,并且“每天 AWS 增加的物理服务器容量相当于 2004 年支持 Amazon.com 所需的容量”,当时 Amazon.com 的年收入为 70 亿美元(汉密尔顿,2014)。

图6.21展示了AWS区域和可用区(右侧纵轴)随时间的增长情况。大多数区域有两个或三个可用区。每个可用区可以有一个或多个WSC,其中最大的可用区拥有超过10个WSC。每个WSC至少有50,000台服务器,最大的WSC则拥有超过80,000台服务器(Hamilton, 2014)。基于对2014年AWS服务器数量的两项已发布估计(Clark, 2014;Morgan 2014),我们预计每年的服务器数量(左侧纵轴)和WSC数量(右侧纵轴)与实际可用区数量的关系。
检查这些估计有效性的一种方法是查看投资情况。亚马逊在2013年至2015年间在物业和设备上的资本投资达到240亿美元,其中有一种估计认为该投资的三分之二用于AWS(Gonzalez and Day 2016;Morgan 2016)。假设建设一个新的WSC需要一年时间。从图6.21的估计来看,2014至2016年的WSC数量为34到51个。每个AWS WSC的成本将为3.1亿到4.7亿美元。汉密尔顿指出,“即使是中等规模的数据中心(WSC)的成本也很可能超过2亿美元。”(Hamilton, 2017)。他进一步表示,目前云服务提供商拥有“O(102)”个WSC;而图6.21的估计是84到126个AWS WSC。尽管这些估计存在不确定性,但它们似乎出奇地一致。他还预测,为了满足未来的需求,最大的云服务提供商最终将增加到“O(105)”个WSC,即比现在多1000倍!
无论云中有多少服务器和WSC,有两个跨越性的问题影响着WSC的成本性能,从而影响云计算,这就是WSC网络以及服务器硬件和软件的效率。
6.6 Cross-Cutting Issues
网络设备是数据中心的SUV。
——詹姆斯·汉密尔顿(2009)
### 防止WSC网络成为瓶颈

图6.22显示了谷歌的网络需求每12到15个月翻一番,导致谷歌WSC舰队中的服务器在短短7年内流量增长了50倍。显然,如果不加以谨慎管理,WSC网络很容易成为性能或成本瓶颈。
在前一版中,我们指出,数据中心的交换机可能花费近100万美元,价格是机架顶部交换机的50倍以上。这种交换机不仅昂贵,过度订阅还影响了软件设计以及服务和数据在WSC中的布局。WSC网络瓶颈限制了数据放置,从而使WSC软件变得复杂。由于这些软件是WSC公司最宝贵的资产之一,因此这种复杂性所带来的成本是相当显著的。
理想的WSC网络应是一个黑箱,其拓扑和带宽并不重要,因为没有限制:任何工作负载都可以放置在任何地方,并针对服务器利用率而不是网络流量本地性进行优化。Vahdat等人(2010)提出借鉴超级计算中的网络技术,以克服价格和性能问题。他们提出了一种网络基础设施,能够扩展到10万个端口和1 Pbit/s的二分带宽。这些新型数据中心交换机的一个主要好处是可以简化因过度订阅而带来的软件挑战。
自那时以来,许多拥有WSC的公司设计了自己的交换机以应对这些挑战(汉密尔顿,2014)。Singh等人(2015)报道了谷歌WSC内部使用的几代定制网络,图6.23列出了这些网络。

图6.23 谷歌WSC中部署的六代网络交换机(Singh等人,2015年)。四柱机架使用商业512口、1 Gbit/s以太网交换机和48口、1 Gbit/s以太网机架顶交换机,使阵列中的服务器数量达到20,000台。Firehose 1.0的目标是为10,000台服务器提供1 Gbps的非阻塞二分带宽,但由于机架顶交换机的连接性较差,导致在链路故障时出现问题。Firehose 1.1是首个定制设计的交换机,具有更好的机架顶交换机连接性。Watchtower和Saturn在同样的基础上进行设计,但使用了新的、更快的商用交换芯片。Jupiter则使用40 Gbps的链路和交换机,提供超过1 Pbit/s的二分带宽。第6.7节将更详细地描述Jupiter交换机以及Clos网络的边缘汇聚和主干模块。
为了降低成本,他们使用标准的商品交换芯片构建交换机。他们发现,传统数据中心交换机的一些功能,如分散的网络路由和管理任意部署场景的协议,部分是用来为其高成本辩护的,但在WSC中并不必要,因为网络拓扑可以在部署前进行规划,并且网络只有一个运营商。谷歌选择使用集中控制,依赖于复制到所有数据中心交换机的共同配置。模块化的硬件设计和强大的软件控制使这些交换机既可用于WSC内部,也可用于WSC之间的广域网络。在十年内,谷歌将其WSC网络的带宽提升了100倍,并在2015年提供了超过1 Pbit/s的二分带宽。
### 在服务器内部高效使用能源
虽然PUE衡量的是WSC的效率,但它对IT设备内部的情况并没有任何描述。因此,另一个电力效率低下的来源是服务器内部的电源,它将高电压输入转换为芯片和磁盘使用的电压。2007年,许多电源的效率在60%到80%之间,这意味着服务器内部的损耗大于从公用电塔的高压线路到服务器的低压线路所经过的多个步骤和电压变化的损耗。一个原因是,电源的功率往往超出主板所需的瓦特数。此外,这些电源通常在25%负载或更低的情况下效率最差,尽管如第441页的图6.3所示,许多WSC服务器在这个负载范围内运行。计算机主板上还配备了电压调节模块(VRMs),它们的效率也相对较低。
Barroso和Hölzle(2007年)表示,整个服务器的目标应该是能源的比例性;也就是说,服务器的能耗应该与所完成的工作量成正比。十年后,我们接近这个理想目标,但仍未达到。例如,第1章中最佳评定的SPECpower服务器在空闲时仍消耗大约20%的全功率,在仅20%负载时几乎消耗50%的全功率。与2007年相比,这代表了巨大的进步,那时空闲计算机消耗60%的全功率,20%负载时消耗70%,但仍有改进的空间。
系统软件设计为在可能提升性能的情况下使用所有可用资源,而不考虑能源的影响。例如,操作系统会使用所有内存来存储程序数据或文件缓存,尽管其中许多数据可能永远不会被使用。软件架构师在未来的设计中需要考虑能源和性能(Carter和Rajamani,2010年)。
在了解了这六个部分的背景后,我们现在可以更好地理解谷歌WSC架构师的工作。
6.7 Putting It All Together: A Google Warehouse-Scale Computer
由于许多拥有WSC(大规模数据中心)的公司在市场上竞争激烈,因此大多数公司不愿意与公众(以及彼此)分享他们最新的创新。幸运的是,谷歌继续其提供关于最近WSC细节的传统,为本书的新版本提供信息,使得这一版很可能是目前关于谷歌WSC的最新公开描述,代表了当前的最先进技术。
### 谷歌WSC的电力分配
我们从电力分配开始。虽然部署中存在许多变体,但在北美,电力通常会经过多次电压变化才能到达服务器,起始于110,000伏以上的高压线路。


对于多个WSC的大型场所,电力通过现场的变电站(如图6.24所示)进行分配。这些变电站的容量达到数百兆瓦。电压会降至10,000伏至35,000伏之间,以便在现场分配给WSC。
在WSC建筑附近,电压进一步降低到约400伏(如图6.25所示),以便分配给数据中心楼层的服务器行。(在北美,常见的电压为480伏,但在世界其他地区为400伏;谷歌使用的是415伏。)为了防止在电力中断时整个WSC掉线,WSC配备了不间断电源(UPS),就像大多数传统数据中心的服务器一样。在这个级别,柴油发电机与电力分配系统连接,以在市电出现问题时提供电力。虽然大多数停电持续时间不到几分钟,但WSC会在现场储存数千加仑的柴油以应对长时间的电力中断。运营商还与当地燃料公司达成协议,以确保在某个场所需要从发电机运行几天或几周时,能够持续供柴油。
在WSC内部,电力通过铜母线槽传送到机架上,母线槽位于每一排机架的上方,如图6.26所示。最后一步将三相电力分成三种独立的单相电力,电压为240–277伏,通过电力电缆送到机架。在机架的顶部附近,电源转换器将240伏交流电转化为48伏直流电,以降低电压至电路板可以使用的水平。
总之,WSC中的电力分配呈现出一种层级结构,每个层级对应一个独特的故障和维护单元:整个WSC、阵列、行和机架。软件能够识别这一层级结构,并根据地形分布工作和存储,以提高可靠性。
世界各地的WSC在电力分配电压和频率上各有不同,但整体设计相似。提高电力效率的主要改进点在于每个步骤中的电压变压器,但这些都是高度优化的组件,因此几乎没有剩余的改进空间。
在谷歌WSC中的冷却
现在我们可以将电力从电力杆传送到WSC的地面,我们需要去除使用过程中产生的热量。在冷却基础设施方面还有相当多的改进机会。
提高能源效率的一个简单方法是让IT设备在更高的温度下运行,这样空气就不需要过于冷却。谷歌的设备在80华氏度(27摄氏度)以上运行,这比传统的数据中心温度要高得多,传统数据中心的温度低得让人需要穿上外套。
为了确保IT设备的气流得到合理规划,谷歌甚至使用了计算流体动力学模拟来设计设施。高效的设计通过减少冷空气与热空气混合的可能性来保持冷空气的温度。例如,今天大多数WSC都通过在交替的机架排中将服务器朝相反方向放置,形成冷热气流交替的过道,从而实现热空气和冷空气的交替。这些过道被称为热通道和冷通道。图6.26展示了一个供人们服务服务器的冷通道,图6.27则展示了热通道。来自热通道的热空气通过管道上升到天花板。

在传统数据中心中,每台服务器依赖内部风扇确保冷空气在热芯片上有足够的流动,以维持其温度。这些机械风扇是服务器中最薄弱的组件之一;例如,风扇的平均故障间隔时间(MTBF)为150,000小时,而硬盘的MTBF则为1,200,000小时。在谷歌的WSC中,服务器风扇与房间内的几十个大型风扇协同工作,以确保整个房间的气流(图6.28)。这种分工意味着小型服务器风扇在最糟糕的功率和环境条件下尽可能少地消耗电力,同时提供最大的性能。大型风扇使用气压作为控制变量进行控制。风扇的转速会被调整,以维持热通道和冷通道之间的最小压力差。

为了冷却这些热空气,他们在机架行的两端添加了大规模的风盘。机架中的热空气通过热通道内的水平风道输送到上方的风盘。(两个机架行共享一对冷却盘管,因为它们位于两个机架行之间的冷通道上方。)冷却后的空气通过天花板上的风道送到图6.28中的大型风扇所在的墙面,这些风扇将冷却后的空气送回包含机架的房间。
我们将很快描述如何去除冷却盘管中的水分,但先让我们回顾一下到目前为止的架构。它将机架与风盘提供的冷却能力分开,从而允许在WSC中跨两个机架行共享冷却。因此,它有效地为高功率机架提供更多冷却,为低功率机架提供更少冷却。在WSC中有数千个机架,它们不太可能完全相同,因此机架之间的功率变化是常见的,而这种设计可以适应这种情况。
冷水通过冷却厂的管道网络供应给各个风盘。通过强制对流,热量传递到冷却盘管中的水中,温水则返回冷却厂。
为了提高WSC(Web服务器集群)的效率,架构师尽可能利用当地环境来去除热量。蒸发冷却塔在WSC中很常见,它们利用外部较冷的空气来冷却水,而不是依靠机械冷却。重要的温度称为湿球温度,这是通过水的蒸发与空气结合所能达到的最低温度。如果空气通过水的蒸发达到饱和(100%相对湿度),则该空气包的温度就是湿球温度,潜热由空气包提供。湿球温度通过在含水的温度计的球头吹气来测量。

在冷却塔内部喷洒温水,并在底部的池中收集,通过蒸发将热量传递给外部空气,从而冷却水。这种技术称为水侧经济化。图6.29显示了冷却塔上方升起的蒸汽。另一种选择是使用冷水而不是清爽的空气。谷歌在芬兰的WSC使用水对水的热交换器,从芬兰湾获取冰冷的水来冷却来自WSC内部的温水。
冷却塔系统利用冷却塔中的蒸发水。例如,一个8兆瓦的设施每天可能需要70,000到200,000加仑的水,因此WSC需要靠近丰富的水源。
尽管冷却厂的设计使得大部分时间可以不依赖人工冷却去除热量,但在天气较热的某些地区,机械冷却机有助于排放热量。
Racks of a Google WSC
我们已经看到谷歌如何为机架供电,以及如何冷却从机架排出的热空气。现在我们准备探索机架本身。图6.30显示了一个典型的谷歌WSC(Web服务器集群)内部机架。为了让这个机架有个背景,WSC由多个阵列(谷歌称之为集群)组成。尽管阵列的大小各异,但有些阵列有一到两打排,每排可容纳二到三打机架。

图6.30 谷歌WSC的机架。其尺寸约为7英尺高、4英尺宽、2英尺深(2米×1.2米×0.5米)。顶级机架交换机确实位于机架的顶部。接下来是电源转换器,使用机架后部的母线将240伏交流电转换为48伏直流电,以供机架中的服务器使用。然后是20个插槽(根据服务器的高度),可以配置用于不同类型的服务器,每个托盘最多可以放置四台服务器。机架底部是高效分布式模块化直流不间断电源(UPS)电池。
图6.30中机架中间显示的20个插槽用于放置服务器。根据宽度的不同,每个托盘可以放置最多四台服务器。机架顶部附近的电源转换器将240伏交流电转化为48伏直流电,这些电流通过机架后部的铜母线供电给服务器。
为整个WSC提供备份电源的柴油发电机需要数十秒才能提供电力。谷歌并没有像早期WSC那样在一个大房间内放置足够的电池来为整个WSC提供几分钟的电力,而是将小型电池放置在每个机架的底部。由于不间断电源(UPS)分布到每个机架,只有在部署机架时才会产生成本,而不是在整个WSC的UPS容量上提前支付。这些电池还优于传统电池,因为它们位于电压转换后的直流侧,并且采用高效的充电方案。此外,用99.99%高效的本地UPS替代94%高效的铅电池,有助于降低电源使用效率(PUE)。这是一种非常高效的UPS系统。
令人安心的是,图6.30中机架的顶部确实包含了我们接下来要描述的顶级机架交换机。
谷歌WSC中的网络
谷歌WSC网络使用一种称为Clos的拓扑,该名称源于发明该拓扑的电信专家(Clos,1953)。图6.31显示了谷歌Clos网络的结构。它是一个多级网络,使用低端口数(“低基数”)的交换机,提供容错功能,并增加网络规模及其切分带宽。谷歌通过在多级网络中增加级数来简单地扩展规模。容错功能则由其固有的冗余性提供,这意味着任何链路的故障对整体网络容量的影响很小。

图6.31 Clos网络有三个逻辑阶段,包含交叉开关:入口阶段、中间阶段和出口阶段。入口阶段的每个输入都可以通过任何中间阶段进行路由,最终到达出口阶段的任意输出。在此图中,中间阶段是M脊块,入口和出口阶段位于N边缘激活块。图6.22展示了谷歌WSC中多个代的Clos网络中脊块和边缘聚合块的变化。
正如第6.6节所述,谷歌使用标准商品交换芯片构建客户交换机,并采用集中控制进行网络路由和管理。每个交换机都获得网络当前拓扑的一致副本,这简化了Clos网络的更复杂路由。
最新的谷歌交换机是Jupiter,它是该交换机的第六代。图6.32显示了交换机的构建模块,图6.33展示了放置在机架中的中间模块的布线。所有电缆都使用光纤束。

Jupiter的商品交换芯片是一个16×16的交叉开关,使用40 Gbps的链路。机架顶部交换机配备了四个这样的芯片,配置有48个40 Gbps的服务器链路和16个40 Gbps的网络结构链路,超额订阅比仅为3:1,优于早期的几代产品。此外,这一代首次为服务器提供了40 Gbps的链路。
图6.32和6.33中的中间模块由16个交换芯片组成。它们采用两个阶段,为机架顶部的连接提供256个10 Gbps的链路,并通过脊与其余网络结构连接64个40 Gbps的链路。机架顶部交换机中的每个芯片通过双冗余10 Gbps链路连接到八个中间模块。
每个聚合块与脊块通过512个40 Gbps链路相连。脊块使用24个交换芯片为聚合块提供128个40 Gbps端口。在最大规模下,它们使用64个聚合块提供双冗余链路。在这个最大尺寸下,切分带宽达到惊人的1.3 Pbit(10^15)每秒。
请注意,整个互联网的切分带宽可能仅为0.2 Pbit/s;其中一个原因是Jupiter的构建是为了实现高切分带宽,而互联网则没有。

### 谷歌WSC中的服务器
现在我们已经了解了如何为WSC提供电源、冷却和通信,终于可以看看实际执行WSC工作任务的计算机。

图6.34中的示例服务器有两个插槽,每个插槽包含一个18核的英特尔Haswell处理器,运行频率为2.3 GHz(参见第5.8节)。照片中显示了16个DIMM,这些服务器通常配置为总共256 GB的DDR3-1600 DRAM。Haswell内存层次结构每个核心有两个32 KiB的L1缓存、一个256 KiB的L2缓存和2.5 MiB的L3缓存,结果是总共有45 MiB的L3缓存。局部内存带宽为44 GB/s,延迟为70 ns,而插槽间带宽为31 GB/s,远程内存延迟为140 ns。Kanev等人(2015)强调了SPEC基准套件和WSC工作负载之间的差异。对于SPEC,几乎不需要L3缓存,但对于真实的WSC工作负载则非常有用。
基线设计配备了一个10 Gbit/s以太网连接的网络接口卡(NIC),尽管也有40 Gbit/s的NIC可用。(其他云服务提供商已经转向25 Gbit/s或其倍数的速度。)虽然图6.34中的照片显示了两个SATA磁盘驱动器,每个驱动器最多可以容纳8 TB,但服务器也可以配置为使用1 TB存储的SSD闪存驱动器。基线服务器的峰值功耗约为150瓦。在图6.30的机架中可以放置四台这样的服务器。
该基线节点还可以扩展为存储节点(或“全磁盘”节点)。第二个单元包含12个SATA磁盘,并通过PCIe连接到服务器。存储节点的峰值功耗约为300瓦。
### 结论
在前一版中,我们描述的谷歌WSC在2011年的PUE为1.23。截至2017年,谷歌16个站点的整体平均PUE降至1.12,其中比利时WSC以1.09的PUE领先。节能技术包括:
- 在较高温度下运行服务器意味着空气只需冷却至80°F(27°C)以上,而不是传统的64–71°F(18–22°C)。
- 较高的冷空气温度目标有助于使设施更频繁地处于冷却塔可以维持的范围内,而冷却塔的能效比传统冷却机组更高。
- 在温带气候中部署WSC,以便在一年中大部分时间仅使用蒸发冷却。
- 为整个房间添加大型风扇,与服务器的小风扇协同工作,以减少能耗,同时满足最坏情况下的需求。
- 通过每排部署冷却管道,将每台服务器的冷却平均到整个机架,以适应较暖和较凉的机架。
- 部署广泛的监测硬件和软件,测量实际PUE与设计PUE的差异,从而提高运营效率。
- 运营的服务器数量超过电力分配系统的最坏情况预测。这是安全的,因为在监测系统的帮助下,同时有成千上万的服务器处于高负载状态的可能性极小,监测系统可以在不太可能发生这种情况时卸载工作(Fan等,2007;Ranganathan等,2006)。PUE得以改善,因为设施的运行接近其完全设计容量,在这一点上效率最高,因为服务器和冷却系统并不成正比。这样的利用率提高减少了对新服务器和新WSC的需求。
我们将拭目以待,还有哪些创新能够进一步提升WSC的效率,使我们成为环境的良好守护者。现在很难想象,工程师们在本书下一版之前如何将WSC的电力和冷却开销减半,就像他们在前一版与这一版之间所做的那样。
6.8 Fallacies and Pitfalls
尽管WSC(Web服务器中心)仅有15年的历史,但像谷歌这样的WSC架构师们已经发现了许多关于WSC的陷阱和谬误,往往是通过艰难的经验得出的。正如我们在引言中所说,WSC架构师是当今的西摩·克雷(Seymour Cray)。
### 谬误:云计算提供商正在亏损。
当AWS(亚马逊网络服务)宣布推出时,一个流行的问题是,云计算在当时低廉的价格下是否有利可图。亚马逊网络服务已经发展到足够庞大,以至于必须在亚马逊的季度报告中单独列出。令一些人感到惊讶的是,AWS已证明是该公司最盈利的部分。2016年,AWS的收入为122亿美元,营业利润率为25%,而亚马逊的零售业务营业利润率则不足3%。AWS始终占据亚马逊利润的四分之三。
### 陷阱:关注平均性能而不是99百分位性能。
正如Dean和Barroso(2013)所观察到的,WSC服务的开发者更关注尾部性能,而不是平均性能。如果一些客户遭遇了糟糕的性能,这种体验可能会驱使他们转向竞争对手,并且他们可能再也不会回来。
### 陷阱:在尝试改善WSC成本效益时使用过于弱小的处理器
阿姆达尔定律(Amdahl's Law)仍然适用于WSC(Web服务器中心)。每个请求都会有一些串行工作,如果这些工作在一个慢速服务器上运行,将会增加请求延迟(Hölzle, 2010;Lim等,2008)。如果串行工作增加了延迟,那么使用弱小处理器的成本必须包括优化代码以恢复到较低延迟的软件开发成本。许多慢速服务器的大量线程也可能更难以调度和负载均衡,因此线程性能的变异性可能导致更长的延迟。当需要等待最长任务时,在1000个任务中出现1/1000的调度不当的机会,可能在10个任务中并不是问题,但在1000个任务中则可能很成问题。
许多较小的服务器也可能导致较低的利用率,因为显然调度较少的任务更容易。最后,甚至一些并行算法在问题被过于细分时也会变得效率低下。谷歌的经验法则是使用服务器级计算机的低端配置(Barroso和Hölzle,2009)。
作为一个具体的例子,Reddi等(2010)比较了运行必应搜索引擎的嵌入式微处理器(Atom)和服务器微处理器(Nehalem Xeon)。他们发现,Atom的查询延迟约是Xeon的三倍。此外,Xeon的表现更为稳健。当Xeon的负载增加时,服务质量逐渐且适度地下降。相比之下,Atom设计在试图承载额外负载时,会迅速违反其服务质量目标。尽管Atom设计更具能效,但响应时间影响收入,而收入损失很可能远大于节省的能量成本。无法达到响应时间目标的节能设计不太可能被部署;我们将在下一章(第7.9节)看到这个陷阱教训的另一种表现。
这种行为直接影响搜索质量。鉴于延迟对用户的重要性,如图6.12所示,必应搜索引擎使用多种策略来优化搜索结果,前提是查询延迟尚未超过设定的截止延迟。较大的Xeon节点具有较低的延迟,这意味着它们可以花更多的时间来优化搜索结果。因此,即使在Atom几乎没有负载的情况下,其在1%的查询中也比Xeon提供了更差的答案。在正常负载下,2%的答案更差。
Kanev等(2015)的研究结果更为近期,但仍然一致。
### 陷阱:不同公司对PUE的测量不一致
谷歌的PUE测量从电力到达变电站之前开始。一些公司在WSC(Web服务器中心)入口处进行测量,这样跳过了表示6%损失的电压降级。此外,如果WSC依赖环境帮助冷却系统,结果也会因季节而异。最后,有些公司报告WSC的设计目标,而不是测量实际系统的结果。最保守且最佳的PUE测量是从公用事业的供电开始,过去12个月测量的PUE的移动平均值。
### 谬论:WSC设施的资本成本高于其所容纳的服务器
尽管在第453页的图6.13上快速浏览可能会导致人们得出这个结论,但这种快速浏览忽略了WSC各个部分的摊销期限。然而,设施的使用寿命为10到15年,而服务器则需要每3到4年更换一次。根据图6.13中分别为10年和3年的摊销时间,十年来的资本支出为设施7200万美元,服务器为3.3(3.3 x 67百万),即2.21亿美元。因此,在十年内,WSC中服务器的资本成本是WSC设施的三倍。
### 陷阱:试图通过非活动低功耗模式节省电力与积极低功耗模式的对比
第441页的图6.3显示,服务器的平均利用率在10%到50%之间。考虑到第6.4节对WSC运营成本的关注,人们会认为低功耗模式将大有帮助。然而,正如第1章所提到的,在这些非活动的低功耗模式下,DRAM或磁盘无法被访问,因此无论速度多么低,它们必须恢复到完全活动模式才能进行读写。问题在于,返回完全活动模式所需的时间和能量使得非活动低功耗模式的吸引力降低。图6.3显示,几乎所有服务器的平均利用率至少为10%,因此可能会预期有较长时间的低活动,但并不会有较长时间的不活动(Lo等人,2014年)。
相比之下,处理器在较低的功耗模式下仍以常规速度的一个小倍数运行,因此积极的低功耗模式更易于使用。请注意,处理器恢复到完全活动模式的时间也是以微秒计,因此积极的低功耗模式也解决了关于低功耗模式的延迟问题。
### 谬论 由于DRAM的可靠性和WSC系统软件的容错能力有了改善,因此在WSC中不需要为ECC内存额外支出。
因为ECC在每64位DRAM中增加了8位,取消错误更正码(ECC)可能会节省DRAM成本的九分之一,特别是考虑到对DRAM的测量声称每兆比特的故障率为1000–5000 FIT(每十亿小时的故障次数)(Tezzaron Semiconductor, 2004)。
Schroeder等人(2009)对Google大多数WSC中配备ECC保护的DRAM进行了研究,这些WSC的服务器数量肯定达到数十万台,研究持续了2.5年。他们发现FIT率比之前公布的高出15至25倍,或每兆比特25000–70000次故障。超过8%的DIMM发生了故障,平均每个DIMM每年有4000个可更正错误和0.2个不可更正错误。在服务器中测得,约三分之一的服务器每年会经历DRAM错误,平均每年有22000个可更正错误和1个不可更正错误。也就是说,对于三分之一的服务器,每2.5小时就会更正一个内存错误。请注意,这些系统使用了更强大的Chipkill编码,而不是更简单的SECDED编码。如果使用了更简单的方案,则不可更正错误的发生率将高出4至10倍。
在仅有奇偶校验错误保护的WSC中,服务器必须在每次内存奇偶校验错误后重新启动。如果重启时间为5分钟,那么三分之一的机器将花费20%的时间在重启上!这种行为将使昂贵设施的性能降低约6%。此外,这些系统将遭受许多不可更正的错误,而操作员并未被通知这些错误的发生。
在早期,Google使用的DRAM甚至没有奇偶校验保护。在2000年,在发布下一个搜索索引版本之前的测试中,它开始对测试查询建议随机文档(Barroso和Hölzle,2009)。原因是某些DRAM中的“固定零”故障,导致新索引损坏。Google增加了一致性检查,以便将来能够检测到此类错误。随着WSC规模的扩大以及ECC DIMM变得更具成本效益,ECC成为Google WSC的标准。ECC还具有在维修过程中更容易找到损坏的DIMM的额外好处。
这些数据说明了为什么Fermi GPU(第4章)在其内存中增加了ECC,而其前身甚至没有奇偶校验保护。此外,这些FIT率对在WSC中使用英特尔Atom处理器的努力产生了怀疑——因为其改善的能效——因为该芯片组不支持ECC DRAM。
### 陷阱 有效应对微秒延迟,而不是纳秒或毫秒延迟。
Barroso等人(2017)指出,现代计算机系统使程序员能够轻松减轻纳秒和毫秒时间尺度上的延迟(例如,缓存和DRAM访问的延迟为几十纳秒,而磁盘访问的延迟为几毫秒),但这些系统在微秒级事件的支持上显著不足。程序员可以通过内存层次结构获得同步接口,硬件通过出色的工作使得这些访问看起来一致且连贯(第2章)。操作系统为程序员提供类似的同步接口以进行磁盘读取,许多操作系统代码行使得在等待磁盘的同时安全地切换到另一个进程,然后在数据准备好时再返回到原始进程。我们需要新的机制来应对闪存等内存技术或100 Gbit/s以太网等快速网络接口的微秒延迟。
### 谬论 在低活动期间关闭硬件可以提高WSC的成本效益。
第454页的图6.14显示,分摊电力分配和冷却基础设施的成本比每月的总电费高出50%。因此,尽管将工作负载集中并关闭空闲机器确实可以节省一些费用,即使节省了一半的电力,每月的运营费用也仅会减少7%。此外,还会面临实际问题,因为广泛的WSC监控基础设施依赖于能够对设备进行监控并观察其响应。能量比例和主动低功耗模式的另一个优点是它们与WSC监控基础设施兼容,这使得一个操作员可以负责超过1000台服务器。还应注意,预防性维护是闲置时间内进行的重要任务之一。
传统的WSC智慧是在活动较少的时期运行其他有价值的任务,以收回电力分配和冷却的投资。一个典型的例子是批处理MapReduce作业,它们为搜索创建索引。另一个通过微薄利用获得价值的例子是AWS上的现货定价,图6.17(第461页)中的例子说明了这一点。对运行任务的时间灵活的AWS用户可以通过让AWS使用现货实例更灵活地调度任务,节省多达四倍的计算费用,例如当WSC的利用率较低时。
6.9 Concluding Remarks
继承建设世界最大计算机的使命,WSC(Web规模计算)计算机的架构师们正在设计未来大部分IT基础设施,以支持移动客户端和物联网(IoT)设备。我们中的许多人每天都多次使用WSC,而在未来十年中,使用WSC的人数和使用频率必然会增加。到目前为止,全球70亿人口中已有超过60亿人拥有手机订阅。随着这些设备变得可以连接互联网,来自世界各地的更多人将能够受益于WSC。
此外,WSC所揭示的规模经济实现了长期以来对计算作为公用事业的梦想。云计算意味着任何有好的想法和商业模式的人,无论身处何地,都可以利用成千上万的服务器几乎瞬间实现他们的愿景。当然,还有一些重要的障碍可能会限制云计算的增长,例如标准、隐私、互联网带宽增长的速度,以及我们在第6.8节中提到的陷阱,但我们预计这些问题将会得到解决,以便云计算能够继续蓬勃发展。
云计算的许多吸引人的特点之一是,它为节能提供了经济激励。由于基础设施投资的成本,云计算提供商很难被说服关闭未使用的设备以节省能源,但很容易说服云计算用户放弃闲置的实例,因为他们正在为这些实例付费,无论它们是否在执行有用的任务。类似地,按使用量收费鼓励程序员高效使用计算、通信和存储,这在没有可理解的定价方案的情况下往往很难实现。明确的定价还使研究人员能够评估成本性能的创新,而不仅仅是性能,因为成本现在容易衡量且可信。最后,云计算意味着研究人员可以在成千上万台计算机的规模上评估他们的想法,而过去只有大型公司才能承担这种规模。
我们相信,WSC正在改变服务器设计的目标和原则,就像移动客户端和物联网的需求正在改变微处理器设计的目标和原则一样。这两者也正在革命化软件行业。每美元性能和每焦耳性能驱动着移动客户端硬件和WSC硬件的发展,而并行性和特定领域加速器是实现这些目标的关键。架构师将在这个激动人心的未来世界的两个方面发挥重要作用。
展望未来,摩尔定律和Dennard缩放的结束(第1章)意味着最新处理器的单线程性能与其前身相比并没有显著提高,这可能会延长WSC中服务器的使用寿命。因此,以前用于替换旧服务器的资金将用于扩展云计算,这可能意味着在下一个十年中,云计算在经济上将比今天更加具有吸引力。摩尔定律时代结合WSC设计和操作的创新,促使WSC的性能-成本-能耗曲线不断改善。随着这个辉煌时代的结束,以及WSC中最大效率低下因素的消除,该领域可能需要寻求芯片架构的创新,以实现持续改进,这是下一章的主题。
7 Domain-Specific Architectures
摩尔定律不可能永远持续下去……我们还有10到20年的时间才能达到一个基本的极限。
——戈登·摩尔,英特尔联合创始人(2005年)
7.1 Introduction
戈登·摩尔不仅在1965年预测了每个芯片上晶体管的惊人增长,开篇的引用也表明,他预测了这一现象在50年后的衰退。作为证据,图7.1显示,即使是他创办的公司——几十年来自豪地将摩尔定律作为资本投资的指导——也正在放缓新半导体工艺的发展。

在半导体繁荣时期,架构师们利用摩尔定律创造了新颖的机制,将大量的晶体管转化为更高的性能。一个五级流水线、32位RISC处理器在1980年代只需大约25,000个晶体管,但其资源增长了100,000倍,以实现加速通用处理器上的通用代码的功能,正如前面的章节所记录的:
- 一级、二级、三级,甚至四级缓存
- 512位SIMD浮点单元
- 15级以上的流水线
- 分支预测
- 超标量执行
- 预测性预取
- 多线程
- 多处理
这些复杂的架构针对的是用高效语言(如C++)编写的百万行程序。架构师们将这些代码视为黑箱,通常对程序的内部结构或它们的功能没有深入了解。像SPEC2017中的基准程序只是测量和加速的工具。编译器作者处于硬件和软件的接口之上,这可以追溯到1980年代的RISC革命,但他们对高层应用行为的理解有限;这就是为什么编译器甚至无法弥合C或C++与GPU架构之间的语义差距。
正如第一章所述,丹纳德缩放(Dennard scaling)结束得比摩尔定律早得多。因此,现在更多的晶体管开关意味着更多的功耗。能源预算并没有增加,我们已经用多个高效核心取代了单个低效处理器。因此,我们在通用架构上没有任何新的手段来继续在成本性能和能效上实现重大改进。由于能源预算是有限的(受电迁移、芯片的机械和热限制影响),如果我们想要更高的性能(更高的每秒操作次数),就需要降低每次操作的能耗。

图7.2是对第一章中提到的内存和逻辑相对能耗的另一种看法,这次是作为算术指令的开销来计算。考虑到这一开销,对现有核心的小幅调整可能会带来10%的性能提升,但如果我们希望实现数量级的改进,同时保持可编程性,就需要将每条指令的算术操作数量从一个增加到数百个。要实现这种效率水平,我们需要从通用核心到领域特定架构(DSA)进行根本性的计算机架构变革。
因此,就像该领域在过去十年中因必要性而从单处理器切换到多处理器一样,绝望也是架构师现在致力于DSA的原因。新常态是,计算机将由标准处理器组成,以运行诸如操作系统等常规大型程序,同时还配备专门执行狭窄范围任务的领域特定处理器,但它们执行这些任务的效率极高。因此,这样的计算机将比以往的均质多核芯片更加异构。
部分论点是,过去几十年利用摩尔定律的架构创新(如高速缓存、乱序执行等)可能并不适合某些领域——特别是在能源使用方面——因此可以回收这些资源,以使芯片更好地与特定领域匹配。例如,高速缓存非常适合通用架构,但对于领域特定架构(DSAs)并不一定合适;对于具有易于预测的内存访问模式或像视频这样的大数据集(几乎没有数据重用)的应用,多级缓存显得过于复杂,占用了可以更好利用的区域和能量。因此,DSAs的承诺在于提高硅效率和更好的能效,后者通常是当今更重要的特性。
架构师可能不会为像SPEC2017基准测试中的编译器那样的大型C++程序创建DSA。领域特定算法几乎总是针对较大系统的小型计算密集型内核,例如用于对象识别或语音理解。DSAs应专注于这部分,而不是计划运行整个程序。此外,改变基准测试的代码不再是违反规则;对于DSAs来说,这是一个完全有效的加速来源。因此,如果想要做出有意义的贡献,感兴趣于DSA的架构师现在必须摒弃局限,学习应用领域和算法。
除了需要扩展他们的专业知识领域外,领域特定架构师面临的另一个挑战是找到一个需求足够大的目标,以证明在系统级芯片(SoC)上或甚至在定制芯片上分配专用硅的合理性。定制芯片和支持软件的非重复工程(NRE)成本会根据制造的芯片数量进行摊销,因此如果只需要1000个芯片,则不太可能在经济上合理。
适应小规模应用的一种方式是使用可重构芯片,如FPGA,因为它们的NRE低于定制芯片,并且多个不同的应用可能能够重用相同的可重构硬件来摊销其成本(参见第7.5节)。然而,由于这些硬件的效率低于定制芯片,因此FPGA带来的收益相对较小。
DSA的另一个挑战是如何将软件移植到其上。像C++编程语言和编译器这样的熟悉编程环境通常不是DSA的合适工具。
本章的其余部分提供了设计DSA的五个指导原则,然后是我们示例领域的教程,即深度神经网络(DNNs)。我们选择DNNs,因为它们正在革新当今计算的许多领域。与某些硬件目标不同,DNNs适用于广泛的问题,因此我们可以重用DNN特定架构来解决语音、视觉、语言、翻译、搜索排名等多个领域的问题。
接下来,我们提供四个DSA的例子:两个加速DNN的定制芯片、一个为数据中心加速多个领域的FPGA,以及一个专为个人移动设备(PMDs)设计的图像处理单元。然后,我们使用DNN基准测试比较这些DSA与CPU和GPU的成本性能,并以对计算机架构即将到来的复兴的预测结束。
7.2 Guidelines for DSAs
以下是一般指导我们在7.4至7.7节中看到的四个领域特定架构(DSA)设计的五个原则。这五条指导原则不仅提高了面积和能效,还提供了两个有价值的附加效果。首先,它们使设计更简单,从而降低了DSA的非重复工程(NRE)成本(见第7.10节中的谬误)。其次,对于与DSA密切相关的用户应用,遵循这些原则的加速器在满足99百分位响应时间截止日期方面比传统处理器的时变性能优化更为匹配,这一点将在第7.9节中看到。图7.3展示了这四个DSA如何遵循这些指导原则。

1. **使用专用内存以最小化数据移动的距离。** 通用微处理器中的多级缓存在尝试为程序最佳移动数据时消耗了大量面积和能量。例如,一个双路集合相联缓存消耗的能量是相应的软件控制的临时存储器的2.5倍。根据定义,DSA的编译器开发者和程序员了解他们的领域,因此硬件无需为他们移动数据。相反,使用专用内存和针对特定功能量身定制的软件控制内存,可以减少数据移动。
2. **将放弃高级微架构优化所节省的资源投入到更多的算术单元或更大的内存中。** 如第7.1节所述,架构师们将摩尔定律带来的丰厚资源转化为对CPU和GPU的资源密集型优化(如乱序执行、多线程、并行处理、预取、地址合并等)。鉴于在这些较狭窄领域内对程序执行的优越理解,这些资源更适合用于更多的处理单元或更大的片上内存。
3. **使用与领域匹配的最简单形式的并行性。** 目标领域的领域特定架构(DSA)几乎总是具有固有的并行性。设计DSA的关键决策是如何利用这种并行性以及如何将其暴露给软件。围绕领域的并行性的自然粒度设计DSA,并在编程模型中简单地暴露这种并行性。例如,针对数据级并行性,如果在该领域中可以使用SIMD,这显然比MIMD对程序员和编译器作者更容易。同样,如果VLIW可以表达该领域的指令级并行性,那么其设计可以比乱序执行更小且更节能。
4. **将数据大小和类型减少到领域所需的最简单形式。** 正如我们将看到的,许多领域的应用通常受限于内存,因此可以通过使用更窄的数据类型来增加有效的内存带宽和片上内存利用率。更窄和更简单的数据还可以让你在相同的芯片面积中容纳更多的算术单元。
5. **使用领域特定编程语言将代码移植到DSA。** 如第7.1节所提到的,DSA面临的一个经典挑战是让应用程序在你的新架构上运行。一个长期存在的误解是,假设你的新计算机如此吸引人,以至于程序员会为你的硬件重写他们的代码。幸运的是,领域特定编程语言在架构师被迫将注意力转向DSA之前就开始流行。例如,Halide用于视觉处理,TensorFlow用于深度神经网络(Ragan-Kelley等,2013;Abadi等,2016)。这些语言使将应用程序移植到你的DSA变得更为可行。如前所述,在某些领域,仅应用程序的一小部分、计算密集型部分需要在DSA上运行,这也简化了移植过程。
DSA引入了许多新术语,主要来自新领域,也来自传统处理器中未见的新架构机制。正如我们在第4章中所做的,图7.4列出了新的缩写、术语和简短解释,以帮助读者理解。

7.3 Example Domain: Deep Neural Networks
人工智能(AI)不仅是计算领域的下一个重大浪潮——它是人类历史上的下一个重大转折点……智能革命将由数据、神经网络和计算能力驱动。英特尔致力于人工智能,因此……我们增加了一系列推动人工智能增长和广泛应用所需的前沿加速器。
——布莱恩·克扎尼奇,英特尔首席执行官(2016)
自本世纪初以来,人工智能(AI)经历了戏剧性的复苏。与其将人工智能构建为一套庞大的逻辑规则,不如将重点转向通过示例数据进行机器学习,作为通向人工智能的途径。学习所需的数据量远比预期的要大。本世纪的仓储级计算机(WSCs)从数十亿用户及其智能手机上收集和存储互联网上的千兆字节信息,提供了充足的数据。我们也低估了从海量数据中学习所需的计算量,但嵌入WSC数千台服务器中的GPU具有出色的单精度浮点性价比,提供了足够的计算能力。
机器学习的一个部分,称为深度神经网络(DNNs),在过去五年里成为了人工智能的明星。DNN在语言翻译方面的突破实例是:DNN在一次飞跃中所取得的进展超过了前十年所有的进步(Tung,2016;Lewis-Kraus,2016);在过去五年中,DNN的应用使得图像识别比赛中的错误率从26%降低至3.5%(Krizhevsky等,2012;Szegedy等,2015;He等,2016);而在2016年,DNN首次使计算机程序击败人类围棋冠军(Silver等,2016)。虽然许多这些应用运行在云端,但它们也使得我们在第一章中提到的智能手机上的谷歌翻译成为可能。2017年,新的重要DNN成果几乎每周都有出现。
对DNN感兴趣的读者可以下载并尝试TensorFlow中的教程(TensorFlow Tutorials,2016),或者对于不太冒险的读者,可以参考一本免费的在线DNN教科书(Nielsen,2016)。
### DNN的神经元
深度神经网络(DNN)的灵感来源于大脑的神经元。用于神经网络的人工神经元简单地计算一组权重(参数)和数据值的乘积之和,然后通过一个非线性函数来确定其输出。正如我们将看到的,每个人工神经元具有很大的输入连接(fan-in)和输出连接(fan-out)。对于图像处理的DNN,输入数据将是照片的像素,像素值与权重相乘。虽然尝试了许多非线性函数,但如今一种流行的函数是 \( f(x) = \max(x, 0) \),如果 \( x \) 为负,则返回0;如果为正或零,则返回原始值。(这个简单的函数被称为整流线性单元,或ReLU。)非线性函数的输出称为激活(activation),因为它是“激活”后的人工神经元的输出。
一组人工神经元可以处理输入的不同部分,该组的输出成为下一层人工神经元的输入。输入层和输出层之间的层称为隐藏层。在图像处理过程中,可以将每一层视为寻找不同类型的特征,从较低级别的特征(如边缘和角度)到较高级别的特征(如眼睛和耳朵)。如果图像处理应用程序试图判断图像中是否包含一只狗,那么最后一层的输出可能是一个介于0和1之间的概率值,或者是对应于一系列狗品种的概率列表。

### 图7.5 六个DNN应用程序,代表了2016年Google在推理任务中95%的DNN工作负载,这些将在第7.9节中使用。列包括DNN名称、DNN中的层数、权重数量以及每个权重的操作数(操作强度)。第595页的图7.41对这些DNN进行了更详细的说明。
层数赋予了DNN这个名称。最初数据和计算能力的不足使得大多数神经网络相对较浅。图7.5展示了多种近期DNN的层数、权重数量以及每个权重所需的操作数。在2017年,一些DNN的层数达到了150层。
### 训练与推理
前面的讨论涉及到已经投入生产的深度神经网络(DNN)。DNN的开发从定义神经网络架构开始,选择层的数量和类型、每层的维度以及数据的大小。虽然专家们可能会开发新的神经网络架构,但大多数从业者会在许多已经存在的设计中进行选择(例如,图7.5),这些设计在与他们的问题类似的情况下表现良好。
一旦选择了神经架构,下一步是学习与神经网络图中每条边相关的权重。权重决定了模型的行为。根据神经架构的选择,单个模型中的权重数量可以从几千到几亿(见图7.5)。训练是调整这些权重的耗时过程,以使DNN能够近似由训练数据描述的复杂函数(例如,将图片映射到该图片中的对象)。
这一开发阶段通常称为训练或学习,而生产阶段有许多名称:推理、预测、评分、实施、评估、运行或测试。大多数DNN使用监督学习,给定一个训练集来学习,其中数据经过预处理以便具有正确的标签。因此,在ImageNet DNN比赛中(Russakovsky等,2015),训练集由120万张照片组成,每张照片被标记为1000个类别之一。其中一些类别非常详细,例如特定品种的狗和猫。获胜者通过评估一个单独的秘密集(包含50,000张照片)来确定,以查看哪个DNN具有最低的错误率。
设置权重是一个迭代过程,使用训练集在神经网络中向后传播。这个过程称为反向传播。例如,由于你知道训练集中狗图像的品种,你会查看你的DNN对该图像的预测,然后调整权重以改善答案。令人惊讶的是,在训练过程开始时,权重应该被设置为随机数据,你只是不断迭代,直到你对使用训练集时DNN的准确性感到满意。
对于数学爱好者来说,学习的目标是找到一个函数,将输入映射到多层神经网络架构中的正确输出。反向传播代表“误差的反向传播”。它计算所有权重的梯度,并作为输入提供给一个优化算法,该算法试图通过更新权重来最小化误差。DNN最流行的优化算法是随机梯度下降(SGD)。它根据反向传播得到的梯度的下降幅度,成比例地调整权重以最大化下降。
对学习更多感兴趣的读者可以参考Nielsen(2016)或TensorFlow教程(2016)。

如图7.6所示,训练可能需要数周的计算时间。推理阶段通常每个数据样本的时间低于100毫秒,这比训练时间短了百万倍。虽然训练所需时间远长于单次推理,但推理的总计算时间是DNN客户数量和他们调用它的频率的乘积。
训练完成后,你部署你的DNN,希望你的训练集能够代表现实世界,并且你的DNN将会如此受欢迎,以至于你的用户使用它的时间远远超过你在开发上投入的时间!
有些任务没有训练数据集,比如试图预测某个现实世界事件的未来。虽然我们在这里不会详细讨论,但强化学习(RL)是一种在2017年非常流行的算法,适用于这种学习。RL不是依赖训练集进行学习,而是作用于现实世界,然后根据该动作是否改善或恶化了情况,从奖励函数中获得信号。
尽管很难想象还有更快速变化的领域,但到2017年,只有三种类型的深度神经网络(DNN)占据了主导地位:多层感知器(MLPs)、卷积神经网络(CNNs)和递归神经网络(RNNs)。它们都是监督学习的例子,依赖于训练集。
### 多层感知器
多层感知器是最早的DNN。每个新层都是前一层所有输出的加权和的非线性函数F:\( y_n = F(W \cdot y_{n-1}) \)。加权和由输出与权重的向量-矩阵乘法组成(见图7.7)。这样的层被称为全连接层,因为每个输出神经元的结果依赖于前一层的所有输入神经元。

图7.7 显示了左侧的输入层 [i21] 和右侧的输出层 [i] 的多层感知器(MLP)。ReLU 是 MLP 中一种流行的非线性函数。输入层和输出层的维度通常是不同的。这样的层称为全连接层,因为它依赖于前一层的所有输入,即使其中许多输入为零。有研究表明,44% 的输入为零,这可能部分是因为 ReLU 将负数转化为零。
我们可以计算每种DNN类型每层的神经元数量、操作数和权重数。对于多层感知器来说,计算是最简单的,因为它只是输入向量与权重数组的向量-矩阵乘法。以下是确定推理中权重和操作数的参数和公式(我们将乘法和加法计算为两个操作):
- **Dim[i]**:输出向量的维度,即神经元的数量
- **Dim[i-1]**:输入向量的维度
- **权重数量**:Dim[i-1] × Dim[i]
- **操作数**:2 × 权重数量
- **操作数/权重**:2
最后一个术语是从第四章讨论的Roofline模型中得出的操作强度。我们使用每个权重的操作数是因为权重数量可以达到数百万,通常无法全部适配到芯片上。例如,MLP中某一层的维度为Dim[i-1] = 4096和Dim[i] = 2048,因此该层的神经元数量为2048,权重数量为8,388,608,操作数量为16,777,216,操作强度为2。正如我们在Roofline模型中回顾到的,低操作强度使得实现高性能变得更加困难。
卷积神经网络(CNN)
卷积神经网络广泛应用于计算机视觉领域。由于图像具有二维结构,相邻的像素是寻找关系的自然位置。CNN 以来自前一层的空间上相邻区域输出的非线性函数作为输入,然后通过权重进行乘法运算,从而多次重用权重。
CNN 的基本思想是每一层都提升图像的抽象级别。例如,第一层可能仅识别水平线和垂直线。第二层可能将这些线组合在一起以识别角点。接下来的步骤可能是识别矩形和圆形。随后的层可以利用这些输入来检测狗的部分特征,如眼睛或耳朵。更高层次则试图识别不同犬种的特征。

图 7.8 CNN 的简化第一步。在这个例子中,输入图像中每组四个像素都与相同的四个权重相乘,以生成输出特征图的单元。所示的模式在输入像素组之间的步幅为二,但其他步幅也是可能的。为了将此图与多层感知器(MLP)联系起来,可以将每个 2×2 的卷积视为一个小型的全连接操作,用于生成输出特征图的一个点。图 7.9 显示了多个特征图如何将这些点转换为第三维的向量。
每个神经层产生一组二维特征图,其中二维特征图的每个单元都试图识别输入相应区域中的一个特征。图 7.8 显示了起始点,在这里,从输入图像进行的 2×2 模板计算生成了第一个特征图的元素。模板计算使用固定模式中的相邻单元来更新数组的所有元素。
输出特征图的数量将取决于你试图从图像中捕获的不同特征的数量以及应用模板时使用的步幅。这个过程实际上更复杂,因为图像通常不仅是单一的、平面的二维层。通常,彩色图像将有三个红、绿和蓝的层。例如,一个 2×2 的模板将访问 12 个元素:2×2 的红色像素、2×2 的绿色像素和 2×2 的蓝色像素。在这种情况下,对于图像三个输入层的 2×2 模板,每个输出特征图需要 12 个权重。

图 7.9 CNN 的一般步骤,左侧显示了 Layer[i-1] 的输入特征图,右侧显示了 Layer[i] 的输出特征图,以及一个三维模板在输入特征图上以生成单个输出特征图。每个输出特征图都有其独特的权重集合,并且每个输出特征图都会进行向量与矩阵的乘法运算。虚线表示该图中未来的输出特征图。如图所示,输入特征图和输出特征图的维度和数量通常是不同的。与多层感知器(MLP)一样,ReLU 是 CNN 中一种流行的非线性函数。
图 7.9 显示了任意数量的输入和输出特征图的一般情况,这发生在第一层之后。该计算是对所有输入特征图进行的三维模板运算,使用一组权重来生成一个输出特征图。
对于数学导向的人来说,如果输入特征图和输出特征图的数量都为 1,且步幅为 1,那么二维 CNN 的单层计算与二维离散卷积相同。
正如我们在图 7.9 中所见,CNN 比 MLP 更复杂。以下是计算权重和操作的参数和公式:
- **DimFM[i-1]**:输入特征图的维度(平方)
- **DimFM[i]**:输出特征图的维度(平方)
- **DimSten[i]**:模板的维度(平方)
- **NumFM[i-1]**:输入特征图的数量
- **NumFM[i]**:输出特征图的数量
- **神经元数量**:NumFM[i] × DimFM[i]²
- **每个输出特征图的权重数量**:NumFM[i-1] × DimSten[i]²
- **每层的权重总数**:NumFM[i] × 每个输出特征图的权重数量
- **每个输出特征图的操作数量**:2 × DimFM[i]² × 每个输出特征图的权重数量
- **每层的操作总数**:NumFM[i] × 每个输出特征图的操作数量 = 2 × DimFM[i]² × NumFM[i] × 每个输出特征图的权重数量 = 2 × DimFM[i]² × 每层的权重总数
- **操作/权重**:2 × DimFM[i]²
在第 7.9 节中的 CNN 有一个层,参数为 DimFM[i-1] = 28、DimFM[i] = 14、DimSten[i] = 3、NumFM[i-1] = 64(输入特征图的数量)和 NumFM[i] = 128(输出特征图的数量)。该层有 25,088 个神经元,73,728 个权重,执行 28,901,376 次操作,操作强度为 392。
我们的例子表明,CNN 层通常比 MLP 中的全连接层具有更少的权重和更高的操作强度。
### 循环神经网络
第三种类型的深度神经网络(DNN)是循环神经网络(RNN),它们在语音识别或语言翻译中非常流行。RNN 通过在 DNN 模型中添加状态来显式建模序列输入,从而增加了记忆事实的能力。这类似于组合逻辑与状态机之间的硬件差异。例如,您可能会学习一个人的性别,而您希望在翻译单词时将这一信息传递给模型以便以后记住。RNN 的每一层都是来自前一层输入的加权和及先前状态的集合。权重在时间步之间重用。
长短期记忆(LSTM)是目前最受欢迎的 RNN。LSTM 解决了之前 RNN 无法记住重要长期信息的问题。
与另外两种 DNN 不同,LSTM 是一种层次化设计。LSTM 由称为单元的模块组成。您可以将单元视为链接在一起以创建完整 DNN 模型的模板或宏,这类似于 MLP 的层排列在一起形成完整 DNN 模型的方式。

图 7.10 LSTM 单元相互连接。输入位于左侧(英文单词),输出位于右侧(翻译后的西班牙单词)。这些单元可以被视为在时间上展开,从上到下。因此,LSTM 的短期和长期记忆通过在展开的单元之间自上而下地传递信息来实现。它们展开得足够多,以便翻译整个句子甚至段落。这种序列到序列的翻译模型会延迟输出,直到输入结束(Wu et al., 2016)。它们按反向顺序生成翻译,使用最近翻译的单词作为下一步的输入,因此“now is the time”变成了“ahora es el momento。”(这个图和下一个图在 LSTM 文献中通常会旋转 90 度,但我们将其旋转为与图 7.7 和 7.8 保持一致。)
图 7.10 显示了 LSTM 单元之间的连接方式。它们从左到右连接,将一个单元的输出连接到下一个单元的输入。它们在时间上也是展开的,在图 7.10 中自上而下运行。因此,在展开循环的每次迭代中,句子以一个单词的形式输入。赋予 LSTM 名称的长期和短期记忆信息也是自上而下地从一个迭代传递到下一个。

图 7.11 这个 LSTM 单元包含 5 次向量-矩阵乘法、3 次元素级乘法、1 次元素级加法和 6 个非线性函数。标准输入和短期记忆输入被连接在一起,形成输入向量-矩阵乘法的向量操作数。标准输入、长期记忆输入和短期记忆输入被连接在一起,形成用于其他四个向量-矩阵乘法中的三个的向量。三个门的非线性函数为 Sigmoid 函数 \(f(x) = \frac{1}{1 + \exp(-x)}\);其他的则是双曲正切函数。(这个图和前一个图在 LSTM 文献中通常会旋转 90 度,但我们将其旋转为与图 7.7 和 7.8 保持一致。)
图 7.11 显示了 LSTM 单元的内容。正如我们从图 7.10 中所预期的,输入位于左侧,输出位于右侧,两个记忆输入位于顶部,两个记忆输出位于底部。
每个单元使用五组独特的权重进行五次向量-矩阵乘法。输入的矩阵乘法与图 7.7 中的 MLP 类似。其他三个被称为门,因为它们限制从一个源传递到标准输出或记忆输出的信息量。每个门传递的信息量由其权重设置。如果权重大多数为零或小值,则很少有信息传递;相反,如果它们大多数为大值,则门允许大多数信息通过。这三个门分别称为输入门、输出门和遗忘门。前两个门过滤输入和输出,而最后一个门则决定沿长期记忆路径要遗忘什么。
短期记忆输出是使用短期权重和该单元输出的向量-矩阵乘法。之所以称为短期,是因为它并不直接使用该单元的任何输入。
由于 LSTM 单元的输入和输出都连接在一起,因此三个输入输出对的大小必须相同。观察单元内部,有足够的依赖关系使得所有输入和输出的大小通常是相同的。
我们假设它们的大小都相同,称为 Dim。
尽管如此,向量-矩阵乘法的大小并不完全相同。三个门的向量乘法的向量是 \(3 \times \text{Dim}\),因为 LSTM 将所有三个输入连接在一起。输入乘法的向量是 \(2 \times \text{Dim}\),因为 LSTM 将输入与短期记忆输入连接在一起作为向量。最后一次乘法的向量只是 \(1 \times \text{Dim}\),因为它只是输出。
现在我们终于可以计算权重和操作数了:
- 每个单元的权重数:\(3 \times (\text{3 Dim} \times \text{Dim}) + (2 \times \text{Dim} \times \text{Dim}) + (1 \times \text{Dim} \times \text{Dim}) = 12 \times \text{Dim}^2\)
- 每个单元的 5 次向量-矩阵乘法的操作数:\(2 \times \text{每个单元的权重数} = 24 \times \text{Dim}^2\)
- 3 次元素级乘法和 1 次加法的操作数(向量的大小与输出相同):\(4 \times \text{Dim}\)
- 每个单元的总操作数(5 次向量-矩阵乘法和 4 次元素级操作):\(24 \times \text{Dim}^2 + 4 \times \text{Dim}\)
- 操作数/权重:\(2\)
对于第 7.9 节中的 LSTM 六个单元之一,\(\text{Dim}\) 为 1024。它的权重数量为 12,582,912,操作数为 25,169,920,操作强度为 2.0003。因此,LSTM 与 MLP 类似,通常拥有更多的权重,并且相比 CNN,操作强度较低。
### 批次
由于深度神经网络(DNNs)通常拥有许多权重,因此一种性能优化策略是对一组输入在从内存中获取权重后进行重用,从而提高有效操作强度。例如,一个图像处理的深度神经网络可能同时处理32张图像,以将获取权重的有效成本降低32倍。这种数据集称为批次(batches)或小批次(minibatches)。除了提高推理性能外,反向传播也需要一批示例而不是逐个示例,以便进行良好的训练。
在图7.7中观察一个多层感知器(MLP),批次可以被视为一系列输入行向量,可以将其视为一个高度维度与批次大小匹配的矩阵。在图7.11中,LSTM的五个矩阵乘法的输入行向量序列也可以视为一个矩阵。在这两种情况下,将它们作为矩阵计算,而不是按顺序作为独立向量计算,可以提高计算效率。
### 量化
对于DNN来说,数值精度的重要性不如许多其他应用那样高。例如,不需要使用双精度浮点运算,而双精度浮点运算是高性能计算的标准。甚至不清楚是否需要IEEE 754浮点标准的完整精度,该标准旨在在浮点数尾数的最后一位内达到半个单位的精度。
为了利用数值精度的灵活性,一些开发者在推理阶段使用定点数而非浮点数。(训练几乎总是使用浮点运算。)这种转换称为量化,经过这种转换的应用程序被称为量化应用(Vanhoucke等,2011)。定点数据的位宽通常为8或16位,标准的乘加操作的累积宽度是乘法的两倍。这种转换通常发生在训练后,并且可能会使DNN的准确性降低几个百分点(Bhattacharya和Lane,2016)。
### DNN的总结
即使是这个简要概述也表明,针对DNN的数字信号处理器(DSAs)需要至少能够良好执行这些面向矩阵的操作:向量-矩阵乘法、矩阵-矩阵乘法和模板计算(stencil computations)。它们还需要支持非线性函数,至少包括ReLU、Sigmoid和tanh。这些适度的要求仍然留下了非常大的设计空间,接下来的四个部分将对此进行探索。
7.4 Google’s Tensor Processing Unit, an Inference Data Center Accelerator
### 张量处理单元(TPU)
张量处理单元(TPU)是谷歌首个为数据中心(WSCs)定制的专用集成电路(ASIC)数字信号处理器(DSA)。其应用领域为深度神经网络(DNNs)的推理阶段,使用为DNN设计的TensorFlow框架进行编程。首个TPU于2015年部署在谷歌的数据中心。
TPU的核心是一台65,536(256 × 256)8位算术逻辑单元(ALU)矩阵乘法单元,以及一块大型软件管理的片上内存。TPU的单线程、确定性执行模型与典型DNN推理应用的99百分位响应时间要求非常匹配。
### TPU的起源
早在2006年,谷歌工程师就开始讨论在其数据中心部署GPU、FPGA或定制ASIC的问题。他们得出结论,能够在特殊硬件上运行的少数应用程序,利用大型数据中心的过剩计算能力几乎可以免费完成,而这种免费服务是很难改进的。
2013年,这一讨论发生了变化。当时预测,如果人们每天使用语音识别DNN进行三分钟的语音搜索,将需要谷歌的数据中心翻倍,以满足计算需求。使用传统CPU来满足这一需求将非常昂贵。于是,谷歌启动了一个高优先级项目,迅速生产用于推理的定制ASIC(并为训练购买现成的GPU)。目标是使成本性能比GPU提高10倍。基于这一任务,TPU在短短15个月内完成了设计、验证(Steinberg,2015)、制造并部署在数据中心。
### TPU架构
为了减少延迟部署的可能性,TPU被设计为PCIe I/O总线上的协处理器,这使得它可以插入现有的服务器中。此外,为了简化硬件设计和调试,主机服务器通过PCIe总线直接向TPU发送指令进行执行,而不是让TPU去获取指令。因此,TPU在本质上更接近浮点单元(FPU)协处理器,而不是从其内存中获取指令的GPU。

图7.12显示了TPU的框图。主CPU通过PCIe总线将TPU指令发送到指令缓冲区。内部模块通常通过256字节宽(2048位)的路径连接在一起。从右上角开始,矩阵乘法单元是TPU的核心。它包含256 × 256个算术逻辑单元(ALU),能够对有符号或无符号整数执行8位乘法和加法。16位的乘积被收集到矩阵单元下方的4 MiB 32位累加器中。当使用8位权重和16位激活(或反之)混合时,矩阵单元的计算速度减半;当两者均为16位时,计算速度降至四分之一。它每个时钟周期读取和写入256个值,并可以执行矩阵乘法或卷积。非线性函数由激活硬件计算。
矩阵单元的权重通过片上权重FIFO进行分级,从一个名为权重内存(Weight Memory)的8 GiB外部DRAM中读取(对于推理,权重是只读的;8 GiB支持多个同时激活的模型)。中间结果存储在24 MiB的片上统一缓冲区中,该缓冲区可以作为矩阵乘法单元的输入。一个可编程DMA控制器负责在CPU主存和统一缓冲区之间传输数据。
### TPU指令集架构
由于指令通过相对较慢的PCIe总线传输,TPU指令遵循CISC传统,包括一个重复字段。TPU没有程序计数器,也没有分支指令;指令由主机CPU发送。这些CISC指令的每条指令时钟周期(CPI)通常为10至20。总共有大约十几条指令,但以下五条是关键指令:
1. **Read_Host_Memory**:将数据从CPU主存读取到统一缓冲区(Unified Buffer)中。
2. **Read_Weights**:将权重从权重内存(Weight Memory)读取到权重FIFO中,以作为矩阵单元的输入。
3. **MatrixMultiply/Convolve**:使矩阵乘法单元执行矩阵-矩阵乘法、向量-矩阵乘法、逐元素矩阵乘法、逐元素向量乘法或从统一缓冲区到累加器的卷积操作。一个矩阵操作接受一个可变大小的B×256输入,将其乘以一个256×256的常量输入,并生成一个B×256的输出,完成所需的B个流水线周期。例如,如果输入是4个256元素的向量,B将为4,因此完成所需的时钟周期为4个。
4. **Activate**:执行人工神经元的非线性函数,支持ReLU、Sigmoid、tanh等选项。其输入为累加器,输出为统一缓冲区。
5. **Write_Host_Memory**:将数据从统一缓冲区写入CPU主存。
其他指令包括备用的主机内存读/写、设置配置、两种版本的同步、中断主机、调试标记、空操作(nop)和停止(halt)。CISC矩阵乘法指令为12字节,其中3字节为统一缓冲区地址;2字节为累加器地址;4字节为长度(有时为卷积的2个维度);其余为操作码和标志。
其目标是在TPU中运行整个推理模型,以减少与主机CPU的交互,并灵活匹配2015年及以后深度神经网络的需求,而不仅仅是满足2013年DNN的要求。
### TPU 微架构
TPU的微架构理念是保持矩阵乘法单元的忙碌状态。计划通过将其他指令的执行与矩阵乘法指令的执行重叠来隐藏这些指令的执行。因此,前面提到的四类通用指令有各自独立的执行硬件(其中主机内存的读/写操作被合并到同一个单元中)。为了进一步增加指令并行性,`Read_Weights`指令遵循解耦访问/执行的理念(Smith, 1982b),即它们可以在发送地址后、权重从权重内存中提取之前完成。矩阵单元具有来自统一缓冲区和权重FIFO的“未就绪”信号,如果这些数据尚不可用,将导致矩阵单元停滞。
需要注意的是,TPU指令可以执行多个时钟周期,而不像传统的RISC流水线那样每个阶段仅需一个时钟周期。
由于读取大型SRAM比进行算术运算要昂贵得多,矩阵乘法单元采用了脉动执行(systolic execution)来节省能源,通过减少对统一缓冲区的读写操作(Kung 和 Leiserson,1980;Ramacher等,1991;Ovtcharov等,2015b)。脉动阵列是一种二维的算术单元集合,每个单元独立地计算一个部分结果,输入来自被视为上游的其他算术单元。它依赖于来自不同方向的数据按规律的时间间隔到达阵列中的单元,在那里它们被合并。由于数据以波前的形式通过阵列流动,这类似于血液通过心脏泵送到人体循环系统,因此“脉动”一词源自此。

图7.13 脉动阵列工作的示例,从页面顶部到底部。在这个例子中,六个权重已经在乘加单元内部,这是TPU的常规做法。三个输入以错开的时间进入,以达到所需的效果,在这个例子中,它们显示为从顶部进入。(在TPU中,数据实际上是从左侧进入的。)数组将数据向下传递给下一个单元,并将计算结果传递给右侧的下一个单元。处理结束时,乘积之和位于右侧。图画由Yaz Sato提供。
图7.13展示了脉动阵列的工作原理。底部的六个圆圈是乘加单元,它们用权重 \(w_i\) 初始化。错开的输入数据 \(x_i\) 从上方进入阵列。图中显示的10个步骤代表了从页面顶部到底部的10个时钟周期。脉动阵列将输入数据传递下去,并将乘积与和传递到右侧。随着数据完成通过脉动阵列的路径,所需的乘积和将显现出来。需要注意的是,在脉动阵列中,输入数据只从内存读取一次,输出数据也只写入内存一次。

在TPU中,脉动阵列是旋转的。图7.14显示,权重从顶部加载,输入数据从左侧流入阵列。给定的256元素乘加操作以对角线波前的形式通过矩阵移动。权重被预加载,并与数据块的第一组数据一起生效。控制和数据被流水线处理,给人一种256个输入同时被读取的错觉,并且在一个延迟后,它们更新256个累加器内存的每个位置。从正确性角度来看,软件并不需要了解矩阵单元的脉动特性,但从性能角度来看,软件关注的是该单元的延迟。
### TPU实现

图7.15 TPU芯片的布局图 阴影部分与图7.14相同。浅色数据缓冲区占37%,浅色计算单元占30%,中等I/O占10%,而深色控制部分仅占2%。在CPU或GPU中,控制部分通常更大(并且设计难度更高)。未使用的白色空间是TPU在设计时强调快速流片的结果。
TPU芯片采用28纳米工艺制造,时钟频率为700 MHz。图7.15显示了TPU的布局。尽管具体的芯片尺寸未公开,但其面积小于英特尔Haswell服务器微处理器的二分之一,后者的尺寸为662 mm²。
24 MiB的统一缓冲区几乎占据了芯片面积的三分之一,而矩阵乘法单元占据了四分之一,因此数据通路几乎占据了芯片面积的三分之二。24 MiB的大小部分是为了与芯片上矩阵单元的间距相匹配,鉴于开发周期较短,部分原因是为了简化编译器。控制部分仅占2%。

图7.16展示了TPU在其印刷电路板上的样子,插入到现有服务器的SATA硬盘插槽中。
### TPU软件
TPU软件栈必须与为CPU和GPU开发的软件兼容,以便应用程序能够快速移植。运行在TPU上的应用程序部分通常使用TensorFlow编写,并被编译为可以在GPU或TPU上运行的API(Larabel, 2016)。图7.17展示了一个多层感知机(MLP)部分的TensorFlow代码。

与GPU类似,TPU栈分为用户空间驱动程序和内核驱动程序。内核驱动程序轻量且仅处理内存管理和中断,旨在实现长期稳定性。用户空间驱动程序变化频繁,负责设置和控制TPU的执行,将数据重新格式化为TPU顺序,并将API调用转换为TPU指令,最终生成应用程序二进制文件。用户空间驱动程序在模型首次评估时进行编译,缓存程序映像并将权重映像写入TPU权重内存;第二次及后续评估则以全速运行。
TPU通常能够从输入到输出完全运行大多数模型,最大化TPU计算时间与I/O时间的比率。计算通常是逐层进行的,通过重叠执行,使得矩阵单元能够隐藏大部分非关键路径操作。
### 改进TPU
TPU架构师研究了微架构的变化,以探讨是否能够改进TPU的性能。与浮点单元(FPU)类似,TPU协处理器的微架构相对容易评估,因此TPU架构师创建了一个性能模型,估算了在内存带宽、矩阵单元大小、时钟频率和累加器数量变化时的性能。使用TPU硬件计数器进行的测量发现,模型化的性能与硬件的平均差异在8%以内。

图7.18 性能随着指标从0.25×扩展到4×的变化:内存带宽、时钟频率+累加器、时钟频率、矩阵单元维度+累加器,以及方形矩阵单元的一个维度。这是根据第7.9节中六个DNN应用计算的平均性能。CNN通常受限于计算,而MLP和LSTM则受限于内存。大多数应用从更快的内存中受益,但更快的时钟对性能影响不大,而更大的矩阵单元实际上会降低性能。该性能模型仅适用于在TPU内部运行的代码,并未考虑CPU主机的开销。
图7.18显示了在这些参数在0.25"到4"范围内扩展时TPU性能的敏感性(第7.9节列出了所使用的基准测试)。除了评估仅提升时钟频率(图7.18中的时钟)的影响外,图7.18还绘制了一个设计(时钟+),该设计在提高时钟频率的同时,适当增加累加器的数量,以便编译器可以保持更多的内存引用在运行中。同样,图7.18还绘制了如果累加器数量以某一维度的平方增长(矩阵+),矩阵单元扩展的情况,因为矩阵在两个维度上都会增长,以及仅增加矩阵单元(矩阵)。
首先,增加内存带宽(内存)的影响最大:当内存带宽增加4"时,性能平均提升3" ,因为这减少了等待权重内存的时间。其次,时钟频率对性能的提升影响不大,无论是否增加更多的累加器。第三,无论应用程序是否获得更多的累加器,图7.18中矩阵单元从256" × 256扩展到512" × 512时,平均性能略有下降。这个问题类似于大页面的内部碎片化,但情况更糟,因为这是在两个维度上。
考虑在LSTM1中使用的600" × 600矩阵。使用256" × 256的矩阵单元,需要九个步骤来铺满600" × 600,总共需要18微秒的时间。更大的512" × 512单元只需四个步骤,但每个步骤耗时是四倍,即32微秒的时间。TPU的CISC指令较长,因此解码时间微不足道,无法掩盖从DRAM加载的开销。
基于性能模型的这些见解,TPU架构师接下来评估了一个替代的假设TPU,如果他们有超过15个月的时间在相同的工艺技术下设计,可能会采用该设计。更激进的逻辑综合和块设计可能将时钟频率提高50%。架构师发现,为K80所使用的GDDR5内存设计接口电路,能够将权重内存带宽提高五倍以上。如图7.18所示,将时钟频率提高到1050 MHz,但不改善内存带宽,几乎对性能没有变化。如果时钟保持在700 MHz,但使用GDDR5作为权重内存,则即使考虑到在修订TPU上调用DNN的主机CPU开销,性能仍提高3.2"。同时进行这两项改进并不会进一步提升平均性能。
### 摘要:TPU 如何遵循指导原则
尽管TPU运行在I/O总线上,并且由于相对较少的内存带宽限制了TPU的充分利用,但大数的小部分仍然可以是相对较大的。正如我们在第7.9节中将看到的,TPU在运行DNN推理应用时实现了相较于GPU的十倍成本性能提升。此外,仅通过将TPU的内存技术改为与GPU相同的技术,重新设计的TPU将会快三倍。
解释TPU成功的一种方式是观察它如何遵循第7.2节中的指导原则。
1. **使用专用内存以最小化数据移动距离。**
TPU拥有24 MiB的统一缓冲区,用于存储MLP和LSTM的中间矩阵和向量以及CNN的特征图。它针对每次访问256字节进行了优化。TPU还有4 MiB的累加器,每个累加器宽32位,用于收集矩阵单元的输出并作为计算非线性函数的硬件的输入。8位权重存储在独立的外部权重内存DRAM中,并通过片上权重FIFO进行访问。相比之下,所有这些类型和大小的数据在通用CPU的包容性内存层次结构中的多个级别上都会存在冗余副本。
2. **将从放弃先进微架构优化中节省的资源投入更多的算术单元或更大的内存。**
TPU提供28 MiB的专用内存和65,536个8位ALU,这意味着它拥有约60%的内存和250倍于服务器级CPU的ALU数量,尽管其大小和功耗只有一半(见第7.9节)。与服务器级GPU相比,TPU的片上内存是其3.5倍,ALU数量是其25倍。
3. **使用与领域匹配的最简单形式的并行性。**
TPU通过其256×256的矩阵乘法单元以二维SIMD并行性提供性能,该单元采用内部流水线和脉动组织,并具有简单的重叠执行流水线。相比之下,GPU依赖于多处理、多线程和一维SIMD,而CPU则依赖于多处理、乱序执行和一维SIMD。
4. **将数据大小和类型简化为该领域所需的最简单形式。**
TPU主要计算8位整数,虽然它也支持16位整数并在32位整数中累加。CPU和GPU还支持64位整数以及32位和64位浮点数。
5. **使用领域特定的编程语言将代码移植到DSA。**
TPU使用TensorFlow编程框架进行编程,而GPU依赖于CUDA和OpenCL,CPU则必须运行几乎所有的代码。
7.5 Microsoft Catapult, a Flexible Data Center Accelerator
在谷歌考虑在其数据中心部署定制ASIC的同时,微软也在考虑为其数据中心使用加速器。微软的观点是,任何解决方案必须遵循以下指导原则:
- 必须保持服务器的同质性,以便快速重新部署机器,并避免让维护和调度变得更加复杂,尽管这个概念与领域特定架构(DSAs)的概念有些矛盾。
- 必须能够扩展到可能需要比单个加速器能够容纳的更多资源的应用,而不让所有应用都负担多个加速器。
- 必须具备能源效率。
- 不应成为单点故障,从而导致可靠性问题。
- 必须能够适应现有服务器中的可用备用空间和电力。
- 不能影响数据中心网络的性能或可靠性。
- 加速器必须提高服务器的性价比。
第一条规则防止了只在某些服务器上帮助某些应用的ASIC的部署,而这是谷歌所做的决定。微软启动了一个名为Catapult的项目,在数据中心服务器的PCIe总线上放置FPGA。这些板卡为需要多个FPGA的应用提供了专用网络。计划是利用FPGA的灵活性,根据不同服务器上的不同应用量身定制其使用,并在时间上重新编程同一服务器,以加速不同的应用。这一计划增加了对加速器投资的回报。FPGA的另一个优势是,它们的非重复性工程(NRE)成本应该低于ASIC,这也可以改善投资回报。我们将讨论Catapult的两代设计,展示其如何演变以满足WSC(Web服务器集群)的需求。
FPGA的一个有趣的优势是,每个应用程序——甚至每个应用程序的每个阶段——都可以视为其自己的DSA,因此在本节中,我们可以在一个硬件平台上看到许多新颖架构的示例。
### Catapult的实现与架构

### 图7.19 Catapult板卡设计(A)显示了块图,(B)是该板卡正反面照片,尺寸为10 cm × 9 cm × 16 mm。PCIe和FPGA间网络通过底部的连接器连接,直接插入主板。(C)是服务器的照片,高度为1U(4.45 cm),宽度为标准机架的一半。每台服务器配备两颗12核的Intel Sandy Bridge Xeon CPU、64 GiB的DRAM、2个固态硬盘、4个硬盘驱动器以及一张10-Gbit以太网网络卡。在(C)中右侧的高亮矩形显示了Catapult FPGA板在服务器上的位置。凉爽的空气从(C)左侧吸入,热空气则从右侧排出,经过Catapult板。这个热点和连接器可提供的功率量使得Catapult板的功率限制为25瓦。48台服务器共享一个以太网交换机,该交换机连接到数据中心网络,它们占据了数据中心机架的一半。
图7.19展示了微软为其服务器设计的一款PCIe板卡,该板卡的功率和散热限制为25瓦。这一限制促使微软选择了28纳米的Altera Stratix V D5 FPGA作为Catapult的首次实现。该板卡还配备了32 MiB的闪存,并包含两个总容量为8 GiB的DDR3-1600 DRAM银行。FPGA具有3926个18位的算术逻辑单元(ALU)、5 MiB的片上存储器,以及11 GB/s的DDR3 DRAM带宽。
每个数据中心机架一半的48台服务器都包含一块Catapult板卡。Catapult遵循关于支持需要多个FPGA的应用程序的先前指南,同时不会影响数据中心网络的性能。它增加了一条独立的低延迟20 Gbit/s网络,连接48个FPGA。网络拓扑结构为二维6×8环形网络。
为了遵循避免单点故障的指导原则,该网络可以重新配置,即使其中一个FPGA发生故障也能继续运行。该板卡还在FPGA外的所有存储器上具有SECDED保护,这是在数据中心大规模部署所必需的。
由于FPGA在芯片上使用大量内存以提供可编程性,因此在工艺几何尺寸缩小的情况下,FPGA比ASIC更容易受到单事件干扰(SEUs)影响。Catapult板上的Altera FPGA包含检测和纠正FPGA内部SEU的机制,并通过定期擦除FPGA配置状态来减少SEU发生的可能性。
与数据中心网络相比,独立网络还有一个额外的好处,即降低了通信性能的可变性。网络的不确定性增加了尾部延迟,这对面对终端用户的应用程序特别有害,因此独立网络使得从CPU成功卸载工作到加速器变得更加容易。由于错误率大幅降低,网络拓扑结构明确,该FPGA网络可以运行比数据中心更简单的协议。
需要注意的是,恢复能力在重新配置FPGA时需要谨慎,以确保它们既不会表现为故障节点,也不会导致主服务器崩溃或损坏相邻的FPGA。微软开发了一种高级协议,以确保在重新配置一个或多个FPGA时的安全性。
### Catapult软件
Catapult与TPU之间最大的区别可能在于需要使用硬件描述语言(如Verilog或VHDL)进行编程。正如Catapult的作者所写(Putnam et al., 2016):
> 未来,FPGA在数据中心的广泛采用面临的最大障碍可能是可编程性。FPGA开发仍然需要在寄存器传输级(RTL)中进行大量手动编码和手动调优。

为了减少编程Catapult FPGA的负担,寄存器传输级(RTL)代码被分为“外壳”(shell)和“角色”(role),如图7.20所示。外壳代码类似于嵌入式CPU上的系统库。它包含将在同一FPGA板上的多个应用程序中重用的RTL代码,例如数据调度、CPU与FPGA之间的通信、FPGA与FPGA之间的通信、数据移动、重配置和健康监测。外壳RTL代码占Altera FPGA的23%。角色代码是应用逻辑,由Catapult程序员使用剩余的77% FPGA资源编写。使用外壳的附加好处是能够在各个应用程序之间提供标准的API和标准行为。
### Catapult上的CNN

微软开发了一种可配置的CNN加速器,作为Catapult的一个应用。配置参数包括神经网络层的数量、各层的维度,甚至使用的数值精度。图7.21显示了CNN加速器的框图。其关键特性包括:
- 在运行时可配置设计,无需使用FPGA工具重新编译。
- 为了最小化内存访问,它提供了高效的CNN数据结构缓冲(见图7.21)。
- 一个二维的处理单元(PE)阵列,可以扩展到数千个单元。
图像被发送到DRAM,然后输入到FPGA中的多银行缓冲区。输入数据被发送到多个PE,以执行生成输出特征图的模板计算。控制器(如图7.21左上角所示)协调每个PE的数据流动。最终结果被重新循环到输入缓冲区,以计算CNN的下一层。

与TPU类似,PE的设计旨在作为一个脉动阵列(systolic array)使用。图7.22展示了PE设计的详细信息。
### Catapult上的搜索加速
测试Catapult投资回报的主要应用是微软必应搜索引擎中的一个关键功能——排名(ranking)。该功能用于对搜索结果的顺序进行排序。输出为文档评分,决定文档在呈现给用户的网页上的位置。该算法分为三个阶段:
1. **特征提取(Feature Extraction)**:根据搜索查询从文档中提取数千个有趣的特征,例如查询短语在文档中出现的频率。
2. **自由表达式(Free-Form Expressions)**:计算来自前一个阶段的数千个特征组合。
3. **机器学习评分(Machine-Learned Scoring)**:使用机器学习算法评估前两个阶段的特征,以计算返回给主搜索软件的文档浮点评分。
Catapult实现的排名功能产生的结果与相应的必应软件完全一致,甚至重现了已知的错误!
利用上述指导方针,排名功能不必局限于单个FPGA。排名阶段被分布在八个FPGA上,具体分配如下:
- 一个FPGA负责特征提取。
- 两个FPGA负责自由表达式。
- 一个FPGA进行压缩阶段,提高评分引擎的效率。
- 三个FPGA负责机器学习评分。
剩余的一个FPGA作为备用,用于容错。将多个FPGA用于单个应用效果良好,因为有专用的FPGA网络。

### 图7.23 FPGA实现特征提取阶段的架构 命中向量(hit vector)描述了查询词在每个文档中的位置,首先被输入到命中向量预处理状态机中,然后被拆分为控制和数据令牌。这些令牌并行地发送到43个独特的特征状态机。特征收集网络收集生成的特征和值对,并将其转发到下一个自由表达式(Free-Form Expressions)阶段。
图7.23展示了特征提取阶段的组织结构。它使用43个特征提取状态机并行计算每个文档-查询对的4500个特征。
接下来是自由表达式(Free-Form Expressions)阶段。微软并没有直接在门电路或状态机中实现这些功能,而是开发了一款60核心的处理器,通过多线程来克服长延迟操作。与GPU不同,微软的处理器不需要SIMD执行。它具备三个特性,使其能够匹配延迟目标:
1. 每个核心支持四个同时运行的线程,其中一个线程可以因长操作而阻塞,而其他线程仍然可以继续执行。所有功能单元都是流水线化的,因此它们可以在每个时钟周期接受新的操作。
2. 线程使用优先编码器进行静态优先级排序。具有最长延迟的表达式在所有核心上使用线程插槽0,接下来最慢的在所有核心上使用插槽1,以此类推。
3. 表达式如果过大,无法在单个FPGA分配的时间内完成,可以在用于自由表达式的两个FPGA之间进行拆分。
FPGA的可重编程性带来了一个代价,即其时钟频率比定制芯片低。机器学习评分(Machine-Learned Scoring)利用两种形式的并行性来克服这一劣势。第一种是构建一个匹配应用程序中可用流水线并行性的流水线。对于排名,限制为每个阶段8微秒。第二种并行形式是少见的多指令流单数据流(MISD)并行性,其中大量独立的指令流在单个文档上并行操作。

图7.24 显示了Catapult在给定延迟限制下排名功能的性能。x轴表示Bing排名功能的响应时间。在x轴上,Bing应用程序的95百分位数最大响应时间为1.0,因此右侧的数据点可能具有更高的吞吐量,但到达时间太晚,无法发挥作用。y轴显示了在给定响应时间下,Catapult和纯软件的95%吞吐量。在标准化响应时间为1.0时,Catapult的吞吐量是运行纯软件模式的Intel服务器的1.95倍。换句话说,如果Catapult的吞吐量与Intel服务器在1.0标准化响应时间下的吞吐量相匹配,则Catapult的响应时间减少了29%。
图7.24展示了Catapult上排名功能的性能。正如我们在第7.9节中将看到的,面向用户的应用程序通常有严格的响应时间;如果应用程序错过了截止时间,即使吞吐量再高也没有意义。x轴表示响应时间限制,以1.0为截止点。在这个最大延迟下,Catapult的速度是主机Intel服务器的1.95倍。
### Catapult Version 1 部署
在将数万台服务器填充到整个仓库规模的计算机之前,微软进行了一个17个全机架的测试部署,其中包含17个48英寸、2个或1632个英特尔服务器。Catapult卡和网络链接在制造和系统集成时进行了测试,但在部署时,1632个卡中有7个发生故障(0.43%),3264个FPGA网络链接中有1个(0.03%)存在缺陷。在经过几个月的部署后,没有其他故障发生。
### Catapult Version 2
尽管测试部署成功,微软仍然改变了实际部署的架构,以使Bing和Azure Networking能够使用相同的板和架构(Caulfield等,2016)。V1架构的主要问题在于独立的FPGA网络无法让FPGA识别和处理标准的以太网/IP数据包,这使其无法用于加速数据中心网络基础设施。此外,布线昂贵且复杂,限制在48个FPGA,并且在某些故障模式下流量的重新路由会降低性能,并可能孤立节点。
解决方案是将FPGA逻辑上放置在CPU和网络接口卡(NIC)之间,使所有网络流量都通过FPGA。这种“电缆上的凸起”布局消除了Catapult V1中FPGA网络的许多弱点。此外,它使FPGA能够运行自己低延迟的网络协议,允许它们被视为数据中心及其跨数据中心的所有FPGA的全局池。
在V1和V2之间发生了三个变化,以克服最初担心Catapult应用会干扰数据中心网络流量的问题。首先,数据中心网络从10 Gbit/s升级到40 Gbit/s,增加了余量。其次,Catapult V2为FPGA逻辑添加了速率限制器,确保FPGA应用不会淹没网络。最后,也是可能最重要的变化是,网络工程师现在有了自己的FPGA使用案例,考虑到其“电缆中的凸起”布局。这种布局使这些以前的旁观者转变为热情的合作者。
通过在大多数新服务器中部署Catapult V2,微软实际上拥有一个由分布式FPGA组成的第二台超级计算机,它与CPU服务器共享相同的网络线路,且规模相同,因为每台服务器配备一个FPGA。图7.25和7.26展示了Catapult V2的框图和电路板。


Catapult V2遵循相同的RTL的外壳和角色分离,以简化编程,但在出版时,由于共享数据中心网络线路的更复杂的网络协议,外壳使用了几乎一半的FPGA资源(44%)。
Catapult V2 用于排名加速和功能网络加速。在排名加速中,微软并没有将几乎所有的排名功能都在 FPGA 内部执行,而是只实现了计算密集型的部分,其余工作留给主机 CPU:
- **特征功能单元(FFU)** 是一组有限状态机,用于测量搜索中的标准特征,例如计算特定搜索词的频率。它的概念与 Catapult V1 的特征提取阶段相似。
- **动态编程特征(DPF)** 使用动态编程创建了一套微软专有的特征,并在某种程度上与 Catapult V1 的自由形式表达阶段相似。
这两者的设计使得它们可以使用非本地 FPGA 来完成这些任务,从而简化了调度。

图 7.27 显示了 Catapult V2 与软件性能的比较,格式与图 7.24 相似。现在,通过put率可以提高 2.25 倍,而不会危及延迟,而之前的加速比为 1.95 倍。
当排名在生产环境中部署并测量时,Catapult V2 的尾部延迟优于软件;也就是说,尽管能够承载两倍的工作负载,但 FPGA 的延迟在任何给定需求下都从未超过软件的延迟。
**摘要:Catapult 如何遵循指导原则**
微软报告称,在试点阶段将 Catapult V1 添加到服务器中,使总拥有成本(TCO)增加了不到 30%。因此,在这个应用中,排名的成本性能净增益至少为 1.95/1.30,或约 1.5 的投资回报率。尽管没有关于 Catapult V2 的 TCO 进行评论,但该板上有相似数量的相同类型芯片,因此我们可以猜测 TCO 并没有更高。如果是这样,Catapult V2 的成本性能约为 2.25/1.30,或 1.75 的排名性能。
以下是 Catapult 如何遵循第 7.2 节的指导原则:
1. **使用专用内存以最小化数据移动的距离。**
Altera V FPGA 具有 5 MiB 的片上内存,应用程序可以根据需要进行定制。例如,对于卷积神经网络(CNN),它用于图 7.21 中的输入和输出特征图。
2. **将通过放弃高级微架构优化节省下来的资源投资于更多的算术单元或更大的内存。**
Altera V FPGA 还具有 3926 个 18 位算术逻辑单元(ALUs),这些单元根据应用程序进行了定制。对于 CNN,它们用于创建驱动图 7.22 中处理单元的脉冲阵列,并形成排名的自由形式表达阶段所使用的 60 核多处理器的数据路径。
3. **使用与领域匹配的最简单形式的并行性。**
Catapult 选择与应用程序匹配的并行性形式。例如,Catapult 对于 CNN 应用程序使用二维 SIMD 并行性,而在机器评分阶段的流排名中使用 MISD 并行性。
4. **将数据大小和类型减少到领域所需的最简单形式。**
Catapult 可以使用应用程序所需的任何大小和类型的数据,从 8 位整数到 64 位浮点数。
5. **使用领域特定的编程语言将代码移植到 DSA。**
在这种情况下,编程是用硬件寄存器传输语言(RTL)Verilog 完成的,这是一种比 C 或 C++ 甚至更低效的语言。由于使用 FPGA,微软未能(也可能无法)遵循此指导原则。尽管该指导原则涉及将应用程序从软件移植到 FPGA 的一次性过程,但应用程序并不是一成不变的。几乎可以说,软件会不断演变,以添加功能或修复错误,尤其是对于像网络搜索这样重要的东西。维护成功程序的成本可能占软件开发成本的大部分。此外,在 RTL 中编程时,软件维护甚至更加繁重。微软的开发人员和其他使用 FPGA 作为加速器的开发人员一样,希望未来领域特定语言和硬件软件协同设计系统的进步能够减少编程 FPGA 的难度。
7.6 Intel Crest, a Data Center Accelerator for Training
Intel CEO 发表的引言出现在第 7.3 节的开头,来自于宣布英特尔将开始为深度神经网络(DNN)推出专用加速器(DSA,简称“加速器”)的新闻发布会。第一个例子是 Crest,这款产品在我们编写本版本时宣布。尽管细节有限,我们还是在这里提到它,因为像英特尔这样的传统微处理器制造商采取这种大胆步骤来拥抱 DSA,具有重要意义。
Crest 主要用于 DNN 训练。英特尔 CEO 表示,目标是在未来三年内将 DNN 训练速度提高 100 倍。图 7.6 显示训练可能需要一个月时间。显然,市场上可能会有需求将 DNN 训练时间缩短到仅八小时,这将比 CEO 预期的速度快 100 倍。
DNN 在未来三年肯定会变得更加复杂,这将需要更大的训练努力。因此,似乎不太可能出现训练提升 100 倍的情况过度的风险。
Crest 指令操作于 32 × 32 的矩阵块上。Crest 使用一种叫做“flex point”的数值格式,这是一种缩放的定点表示:16 位数据的 32 × 32 矩阵共享一个 5 位的指数,这个指数作为指令集的一部分提供。

图 7.28 显示了 Lake Crest 芯片的框图。为了对这些矩阵进行计算,Crest 使用了图 7.28 中的 12 个处理集群。每个集群包括一个大型 SRAM,一个线性代数处理单元,以及少量用于芯片内外路由的逻辑。四个 8 GiB HBM2 DRAM 模块提供 1 TB/s 的内存带宽,这应该为 Crest 芯片提供了一个有吸引力的 Roofline 模型。除了高带宽的主内存路径外,Lake Crest 还支持处理集群内部计算核心之间的高带宽互连,这促进了快速的核心间通信,而不需要经过共享内存。Lake Crest 的目标是在训练上比 GPU 提高 10 倍。
图 7.28 显示了 12 个芯片间连接(ICL)和 2 个芯片间控制器(ICC),因此可以明显看出,Crest 的设计允许多个 Crest 芯片协同工作,这与 Catapult 中连接 48 个 FPGA 的专用网络在精神上相似。训练性能提升 100 倍的目标可能需要将多个 Crest 芯片连接在一起。
7.7 Pixel Visual Core, a Personal Mobile Device Image Processing Unit
Pixel Visual Core 是 Google 开发的可编程、可扩展的专用加速器(DSA),旨在用于图像处理和计算机视觉,最初面向运行 Android 操作系统的手机和平板电脑,未来可能扩展到物联网(IoT)设备。它采用多核设计,支持 2 到 16 个核心,以实现所需的性价比。它可以是独立的芯片,也可以是系统单芯片(SOC)的一部分。与其 TPU 兄弟相比,它的面积和能耗预算要小得多。图 7.29 列出了本节中出现的术语和缩写。

Pixel Visual Core 是我们称之为图像处理单元(IPUs)的一类新的领域特定架构的例子。IPUs 解决了 GPU 的逆问题:它们分析和修改输入图像,而不是生成输出图像。我们称其为 IPUs,表示作为一种 DSA,它们不需要在所有方面都表现良好,因为系统中还会有 CPU(和 GPU)来执行非输入视觉任务。IPUs 依赖于上述提到的用于卷积神经网络(CNN)的模板计算。
Pixel Visual Core 的创新之处在于用二维处理单元(PEs)的阵列替代了 CPU 的一维 SIMD 单元。它们为 PEs 提供了一个二维的移位网络,能够感知元素之间的二维空间关系,并提供一个二维版本的缓冲区,以减少对外部芯片内存的访问。这种新颖的硬件使得执行对视觉处理和 CNN 算法至关重要的模板计算变得容易。
### ISP:IPU 的硬件前身
大多数便携式移动设备(PMD)都配备了多个摄像头进行输入,这导致了用于增强输入图像的硬件加速器——图像信号处理器(ISP)的出现。ISP 通常是一个固定功能的应用特定集成电路(ASIC)。如今几乎每个 PMD 都包含一个 ISP。

图 7.30 显示了图像信号处理器(ISP)、CPU、DRAM、镜头和传感器之间的互连示意图。ISP 将统计信息发送给 CPU,以及改进后的图像,后者可以发送到显示器或存储到 DRAM 中以便后续处理。然后,CPU 处理图像统计信息,并发送信息以使系统进行自适应:将自动白平衡(AWB)发送到 ISP,将自动曝光(AE)发送到传感器,将自动对焦(AF)发送到镜头,这三者合称为 3A。
图 7.30 显示了一个典型的图像处理系统的组织结构,包括镜头、传感器、ISP、CPU、DRAM 和显示器。ISP 接收图像,去除镜头和传感器所带来的图像伪影,插值缺失的颜色,并显著改善图像的整体视觉质量。PMD 的镜头通常较小,因此像素非常细小且噪声较大,因此这一步骤对于生成高质量的照片和视频至关重要。
ISP 通过计算一系列级联算法以光栅扫描顺序处理输入图像,通常采用软件可配置的硬件构建块组织成一个管道,以最小化内存流量。在管道的每个阶段和每个时钟周期中,输入几个像素并输出几个像素。计算通常在小的像素邻域(模板)上进行。各个阶段通过称为行缓冲区的缓冲区连接。行缓冲区通过捕捉足够的完整行中间图像,帮助保持处理阶段的利用率,从而利用空间局部性以便于下一个阶段所需的计算。
增强后的图像要么发送到显示器,要么存储到 DRAM 中以便后续处理。ISP 还会将图像的统计信息(例如,颜色和亮度直方图、清晰度等)发送给 CPU,CPU 进一步处理这些信息,以帮助系统适应。
尽管 ISP 效率高,但它们存在两个主要缺点。考虑到便携设备对图像质量提高的不断需求,第一个缺点是 ISP 的灵活性不足,特别是设计和制造一个新的 ISP 在系统单芯片(SOC)内需要数年时间。第二个缺点是这些计算资源只能用于图像增强功能,而不能满足 PMD 上任何时候所需的其他功能。当前一代 ISP 在 PMD 的功耗预算下能处理高达 2 兆操作每秒的工作负载,因此,DSA 的替代方案必须实现类似的性能和效率。
Pixel Visual Core 软件
Pixel Visual Core 将图像信号处理器(ISP)内核的典型硬件管道组织形式推广为一个有向无环图(DAG)。Pixel Visual Core 的图像处理程序通常使用 Halide 编写,Halide 是一种针对图像处理的领域特定函数式编程语言。图 7.31 是一个 Halide 示例,用于模糊图像。Halide 包含一个函数部分,用于表达正在编程的功能,还有一个单独的调度部分,用于指定如何将该功能优化到底层硬件上。

### Pixel Visual Core 架构理念
PMD 的功耗预算为 6-8 瓦特,持续 10-20 秒,然后在屏幕关闭时降至几十毫瓦。考虑到 PMD 芯片的能源目标非常具有挑战性,Pixel Visual Core 架构受到第一章中提到的原始操作的相对能源成本的强烈影响,并在图 7.32 中明确体现。显著的是,单次 8 位 DRAM 访问所需的能量相当于 12,500 次 8 位加法或 7-100 次 8 位 SRAM 访问,这取决于 SRAM 的组织结构。IEEE 754 浮点操作相比于 8 位整数操作的更高成本(22“到 150”),加上存储较窄数据的芯片面积和能量效益,强烈支持在算法能够适应时使用较窄的整数。

除了第 7.2 节中的指导方针外,这些观察还引导了 Pixel Visual Core 设计的其他主题:
- **二维优于一维**:二维组织在处理图像时可能是有益的,因为它最小化了通信距离,并且图像数据的二维和三维特性可以利用这样的组织结构。
- **距离越近越好**:移动数据是昂贵的。此外,将数据移动到 ALU 操作的相对成本正在增加。当然,DRAM 的时间和能量成本远远超过任何本地数据存储或移动的成本。
从 ISP 转向 IPU 的主要目标是通过可编程性获得更多的硬件重用。以下是 Pixel Visual Core 的三个主要特征:
1. 遵循“二维优于一维”的主题,Pixel Visual Core 采用二维 SIMD 架构,而非一维 SIMD 架构。因此,它具有一个独立处理元素(PE)的二维数组,每个 PE 包含 2 个 16 位 ALU、1 个 16 位 MAC 单元、10 个 16 位寄存器和 10 个 1 位谓词寄存器。16 位算术遵循提供领域所需精度的指导方针。
2. Pixel Visual Core 在每个 PE 需要临时存储。遵循第 7.2 节中避免使用缓存的指导方针,这种 PE 存储是编译器管理的暂存内存。每个 PE 内存的逻辑大小为 128 个 16 位条目,即仅 256 字节。由于在每个 PE 中实现单独的小 SRAM 效率低,Pixel Visual Core 而是将 8 个 PE 的内存组合在一个宽的 SRAM 块中。由于 PEs 以 SIMD 方式操作,Pixel Visual Core 可以将所有单个读写操作绑定在一起,形成一个“更方正”的 SRAM,这比狭窄且深或宽且浅的 SRAM 更高效。图 7.33 显示了四个 PEs。

3. 为了能够在所有 PE 中执行同时的模板计算,Pixel Visual Core 需要从最近的邻居收集输入。这种通信模式需要一个“ NSEW”(北、南、东、西)移位网络:它可以在任何方向上批量移动 PE 之间的数据。为了在图像移动时不丢失边缘像素,Pixel Visual Core 将网络的端点连接在一起,形成一个环面。
需要注意的是,移位网络与 TPU 和 Catapult 中的处理元素数组的脉动阵列形成对比。在这种情况下,软件明确地将数据向所需方向移动,而脉动方法是由硬件控制的二维管道,以波前的形式移动数据,这对软件是不可见的。
### Pixel Visual Core Halo
一个 3×3、5×5 或 7×7 的模板将从正在计算的二维子集边缘的 1、2 或 3 个外部像素获取输入(模板尺寸的一半减去半个像素的维度)。这留下了两个选择:要么 Pixel Visual Core 在边界附近的元素中未充分利用硬件,因为它们仅传递输入值,要么 Pixel Visual Core 稍微扩展二维处理元素(PE),使用简化的 PE,而不包含算术逻辑单元(ALU)。由于标准 PE 和简化 PE 之间的尺寸差异约为 2.2“,因此 Pixel Visual Core 拥有一个扩展数组。这个扩展区域被称为光晕(halo)。图 7.34 显示了一个 8×8 PE 数组周围的两行光晕,并说明了左上角的 5×5 模板计算如何依赖于光晕。

### 图 7.34 这个图展示了一个由完整处理元素(以未阴影的圆圈表示)组成的二维数组,周围环绕着两层简化处理元素(以阴影的菱形表示),称为光晕(halo)。在这个图中,有 8×8 或 64 个完整的 PE,光晕中有 80 个简化 PE。(Pixel Visual Core 实际上有 16×16 或 256 个完整 PE,以及两层光晕,因此有 144 个简化 PE。)光晕的边缘连接在一起(以灰色线条表示),形成一个环面。Pixel Visual Core 在所有处理元素之间进行一系列的二维移动,以将每个模板计算的邻近部分移入模板的中心 PE。左上角展示了一个 5×5 的模板示例。请注意,25 个数据点中有 16 个来自光晕处理元素。
### Pixel Visual Core 的处理器
16×16 个处理元素(PE)以及每个维度中的 4 条光晕通道的集合称为 PE 数组或向量数组,是 Pixel Visual Core 的主要计算单元。它还具有一个称为 Sheet Generator (SHG) 的负载-存储单元。SHG 指的是对 1×1 到 256×256 像素块的内存访问,这些块称为“表单”(sheets)。这一过程发生在下采样过程中,典型的值为 16×16 或 20×20。
Pixel Visual Core 的实现可以根据可用资源拥有任意偶数个 2 个或更多的核心。因此,它需要一个网络将这些核心连接在一起,因此每个核心也有一个与芯片网络(Network on Chip,NOC)的接口。然而,Pixel Visual Core 的典型 NOC 实现并不会采用昂贵的交叉开关,因为那需要数据在较长距离内传输,而这会增加成本。利用应用程序的管道特性,NOC 通常只需要与相邻的核心进行通信。它被实现为一个二维网格,这样可以在软件控制下实现对成对核心的电源门控。
最后,Pixel Visual Core 还包括一个称为标量通道(scalar lane,SCL)的标量处理器。它与向量通道相同,只是增加了处理跳转、分支和中断的指令,控制指令流向向量数组,并调度所有针对表单生成器的加载和存储。它还有一个小型指令内存。请注意,Pixel Visual Core 具有单一的指令流来控制标量和向量单元,类似于 CPU 核心如何为其标量和 SIMD 单元提供单一的指令流。
除了核心之外,还有一个 DMA 引擎用于在 DRAM 和行缓冲区之间传输数据,同时高效地在图像内存布局格式之间进行转换(例如,打包/解包)。除了顺序访问 DRAM,DMA 引擎还执行类似向量的 DRAM 聚合读取,以及顺序和跨步的读取和写入。
### Pixel Visual Core 指令集架构
像 GPU 一样,Pixel Visual Core 采用两步编译过程。第一步是将目标语言(例如 Halide)中的程序编译为 vISA 指令。Pixel Visual Core 的 vISA(虚拟指令集架构)部分受到 RISC-V 指令集的启发,但它使用特定于图像的内存模型,并扩展指令集以处理图像处理,特别是二维图像的概念。在 vISA 中,核心的二维数组是无限的,寄存器的数量没有限制,内存大小同样没有限制。vISA 指令包含纯函数,这些函数不会直接访问 DRAM(见图 7.36),这大大简化了将它们映射到硬件上的过程。

下一步是将 vISA 程序编译为 pISA(物理指令集架构)程序。将 vISA 作为编译器的目标,使得处理器能够与过去的程序软件兼容,同时接受 pISA 指令集的更改,因此 vISA 在功能上与 GPU 的 PTX 相似(见第 4 章)。从 vISA 降级到 pISA 包括两个步骤:编译和使用早绑定参数的映射,然后使用晚绑定参数修补代码。必须绑定的参数包括 STP 大小、光晕大小、STP 数量、行缓冲区的映射、内核到处理器的映射,以及寄存器和局部内存分配。

图 7.35 显示 pISA 是一个非常长的指令字(VLIW)指令集,指令宽度为 119 位。前 43 位字段用于标量通道,接下来的 38 位字段指定由二维 PE 数组进行的计算,第三个 12 位字段指定二维 PE 数组的内存访问。最后两个字段是用于计算或寻址的立即数。所有 VLIW 字段的操作都是您所期望的:二进制补码整数运算、饱和整数运算、逻辑运算、位移、数据传输,以及一些特殊操作,如除法迭代和计数前导零。标量通道支持二维 PE 数组中的操作超集,并添加了用于控制流和表单生成器控制的指令。上述提到的 1 位谓词寄存器使得条件移动到寄存器成为可能(例如,若 C,则 A = B)。
尽管 pISA VLIW 指令非常宽,但 Halide 内核通常较短,通常只有 200 到 600 条指令。请记住,作为一个 IPU,它只需要执行应用程序的计算密集部分,其余的功能则留给 CPU 和 GPU。因此,Pixel Visual Core 的指令存储器仅保存 2048 条 pISA 指令(28.5 KiB)。
标量通道发出访问行缓冲区的指令生成器指令。与 Pixel Visual Core 内部的其他内存访问不同,这些访问的延迟可能超过 1 个时钟周期,因此它们具有类似 DMA 的接口。通道首先在特殊寄存器中设置地址和传输大小。
**Pixel Visual Core 示例**

图 7.36 显示了从 Halide 编译器输出的 vISA 代码,针对图 7.31 中的模糊示例,添加了注释以便于理解。它首先在 x 方向计算模糊,然后在 y 方向使用 16 位算术进行计算。该 vISA 代码与 Halide 程序的功能部分相匹配。这段代码可以被视为在图像的所有像素上执行。
### Pixel Visual Core 处理单元
架构设计的一个决策是如何构建 halo。Pixel Visual Core 使用 16×16 的处理单元(PE),并增加了 2 个额外元素的 halo,因此它可以直接支持 5×5 的模板。请注意,PE 数组越大,支持特定模板大小的 halo 开销就越小。
对于 Pixel Visual Core,halo PE 的较小尺寸和 16×16 数组意味着 halo 的面积仅增加 20%。对于 5×5 的模板,Pixel Visual Core 每个时钟周期可以计算 1.8 倍的结果(162/122),而对于 3×3 的模板,比例为 1.3(162/142)。
PE 的算术单元设计是以乘加(MAC)为驱动的,乘加是模板计算的一种基本运算。Pixel Visual Core 的本地 MAC 在乘法时为 16 位宽,但可以以 32 位宽进行累加。由于读写额外的流水线寄存器会不必要地消耗能量,因此流水线 MAC 会影响时钟周期。乘法加法硬件决定了时钟周期。其他操作是之前提到的传统逻辑和算术运算,以及饱和版本的算术运算和一些专用指令。
PE 具有两个 16 位的算术逻辑单元(ALU),可以在单个时钟周期内以多种方式操作:
- 独立操作,产生两个 16 位的结果:A op B, C op D。
- 融合操作,产生一个 16 位的结果:A op (C op D)。
- 结合操作,产生一个 32 位的结果:A:C op B:D。
### 二维行缓冲区及其控制器
由于 DRAM 访问消耗了大量能量(见图 7.32),Pixel Visual Core 的内存系统经过精心设计,以尽量减少 DRAM 访问的次数。关键创新是二维行缓冲区。
内核在逻辑上运行在独立的核心上,并以有向无环图(DAG)的形式连接,输入来自传感器或 DRAM,输出到 DRAM。行缓冲区在内核之间保存正在计算的图像部分。

图 7.37 显示了 Pixel Visual Core 中行缓冲区的逻辑使用。
以下是二维行缓冲区必须支持的四个特性:
1. 必须支持各种大小的二维模板计算,这些大小在设计时是未知的。
2. 由于 halo 的存在,对于 Pixel Visual Core 中的 16×16 PE 数组,STP 希望从行缓冲区读取 20×20 像素块,并将 16×16 像素块写入行缓冲区。(如前所述,他们将这些像素块称为 sheets。)
3. 由于 DAG 是可编程的,我们需要可以由软件在任意两个核心之间分配的行缓冲区。
4. 几个核心可能需要从同一行缓冲区读取数据。因此,行缓冲区应支持多个消费者,但只需一个生产者。
Pixel Visual Core 中的行缓冲区实际上是建立在相对大量的 SRAM 之上的多读者、二维 FIFO 抽象:每个实例 128 KiB。它包含仅使用一次的临时“图像”,因此小型、专用的本地 FIFO 比远程内存中的缓存要高效得多。
为了适应读取 20×20 像素块和写入 16×16 像素块之间的大小不匹配,FIFO 中的基本分配单位是 4×4 像素组。每个模板处理器有一个行缓冲池(LBP),可以有八个逻辑行缓冲区(LB),以及一个用于 DMA 的 I/O LBP。
LBP 具有三个抽象层次:
1. 在顶部,LBP 控制器支持作为逻辑实例的八个 LB。每个 LB 有一个 FIFO 生产者和最多八个 FIFO 消费者。
2. 控制器跟踪每个 FIFO 的一组头指针和尾指针。注意,LBP 内部行缓冲区的大小是灵活的,由控制器决定。
3. 在底部,有许多物理内存块以支持带宽需求。Pixel Visual Core 具有八个物理内存块,每个块具有 128 位接口和 16 KiB 的容量。
LBP 的控制器具有挑战性,因为它必须满足 STP 和 I/O DMA 的带宽需求,并调度它们对物理 SRAM 内存块的所有读写操作。LBP 控制器是 Pixel Visual Core 中最复杂的部分之一。
### Pixel Visual Core 实现

Pixel Visual Core 的第一次实现是作为一个独立芯片。图 7.38 显示了该芯片的布局,芯片拥有 8 个核心。该芯片于 2016 年在 TSMC 28 nm 工艺下制造。芯片尺寸为 6" × 7.2 mm,工作频率为 426 MHz,并与 512 MB DRAM 叠加在一起,形成硅封装(Silicon in Package)。根据工作负载的不同,芯片的功耗(包括 DRAM)在 187 到 4500 mW 之间变化。约 30% 的功耗用于控制的 ARMv7 A53 核心、MIPI、PCIe 和 LPDDR 接口,接口的面积超过了这颗芯片的一半,达到了 23 mm²。

在运行最坏情况下的“功率病毒”时,Pixel Visual Core 的功耗可以高达 3200 mW。图 7.39 显示了一个核心的布局。
### 总结:Pixel Visual Core 如何遵循指导原则
Pixel Visual Core 是一种用于图像和视觉处理的多核数据专用架构(DSA),旨在作为独立芯片或移动设备 SOC 的 IP 模块。正如我们在第 7.9 节中将看到的,它在卷积神经网络(CNN)方面的每瓦性能比 CPU 和 GPU 高出 25 到 100 倍。以下是 Pixel Visual Core 如何遵循第 7.2 节中的指导原则。
1. **使用专用存储器以最小化数据移动的距离。**
Pixel Visual Core 最显著的架构特征之一是软件控制的二维行缓冲区。每个核心有 128 KiB 的行缓冲区,占据了相当大的面积。每个核心还拥有 64 KiB 的软件控制的处理单元(PE)内存用于临时存储。
2. **将省下的资源从放弃先进的微架构优化转投入更多的算术单元或更大的存储器。**
Pixel Visual Core 的另两个关键特性是每个核心有一个 16 × 16 的二维处理单元阵列,以及处理单元之间的二维移位网络。它提供了一个光环区域,充当缓冲区,允许其 256 个算术单元的充分利用。
3. **使用与领域匹配的最简单形式的并行性。**
Pixel Visual Core 依赖于二维 SIMD 并行性,利用其处理单元阵列,使用超长指令字(VLIW)来表达指令级并行性,并使用多程序多数据(MPMD)并行性来充分利用多个核心。
4. **将数据大小和类型简化到领域所需的最简单形式。**
Pixel Visual Core 主要依赖于 8 位和 16 位整数,但它也支持 32 位整数,尽管速度较慢。
5. **使用特定领域的编程语言将代码移植到 DSA。**
Pixel Visual Core 使用领域特定语言 Halide 进行图像处理编程,并使用 TensorFlow 进行卷积神经网络编程。
7.8 Cross-Cutting Issues Heterogeneity and System on a Chip (SOC)
将 DSA 集成到系统中的简单方法是通过 I/O 总线,这也是本章数据中心加速器采用的方法。为了避免通过较慢的 I/O 总线获取内存操作数,这些加速器配备了本地 DRAM。
阿姆达尔定律提醒我们,加速器的性能受限于主机内存和加速器内存之间数据传输的频率。肯定会有一些应用受益于将主机 CPU 和加速器集成到同一个系统芯片(SoC)中,这也是 Pixel Visual Core 及最终 Intel Crest 的目标之一。
这样的设计被称为 IP 块,代表知识产权(Intellectual Property),但一个更具描述性的名称可能是可移植设计块(portable design block)。IP 块通常使用硬件描述语言(如 Verilog 或 VHDL)来指定,以便集成到 SoC 中。IP 块使得市场上出现了许多公司可以生产 IP 块,其他公司则可以购买这些块以构建适用于其应用的 SoC,而不必自己设计所有内容。图 7.40 显示了 IP 块的重要性,通过绘制 Apple PMD SoC 各代的 IP 块数量,可以看到它们在短短四年内增加了三倍。另一个表明 IP 块重要性的例子是,CPU 和 GPU 仅占 Apple SoC 的三分之一面积,而 IP 块则占据了剩余部分(Shao 和 Brooks,2015)。

设计 SoC 就像城市规划,各个独立小组为有限资源进行游说,而找到合适的折衷方案则很困难。CPU、GPU、缓存、视频编码器等具有可调设计,可以缩小或扩大以使用更多或更少的面积和能量,从而提供更多或更少的性能。预算会因 SoC 是用于平板电脑还是物联网而有所不同。因此,IP 块必须在面积、能量和性能上具备可扩展性。此外,新 IP 块提供小资源版本尤其重要,因为它可能尚未在 SoC 生态系统中建立稳固的基础;如果初始资源请求能够适度,采用就会容易得多。Pixel Visual Core 的方法是多核设计,允许 SoC 工程师在 2 到 16 个核心之间选择,以匹配面积、功耗预算和期望性能。
有趣的是,集成的吸引力是否会导致大多数数据中心处理器来自传统的 CPU 公司,且这些 CPU 芯片上集成了 IP 加速器,还是系统公司将继续设计自己的加速器并在其 ASIC 中包含 IP CPU。
### 开放指令集
设计 DSA 的一个挑战是如何与 CPU 协作以运行其余应用程序。如果它要在同一个 SoC 上,那么一个重要的决定就是选择哪个 CPU 指令集,因为直到最近,几乎每个指令集都属于单一公司。过去,SoC 的实际第一步是与一家公司签署合同以锁定指令集。另一种选择是设计自己的定制 RISC 处理器,并将编译器和库移植到上面。许可 IP 核的成本和麻烦导致 SoC 中出现了出乎意料的数量的 DIY 简单 RISC 处理器。一位 AMD 工程师估计,现代微处理器中有 12 种指令集!
RISC-V 提供了第三种选择:一种可行的免费开放指令集,预留了大量操作码空间用于为特定领域的协处理器添加指令,这使得之前提到的 CPU 和 DSA 之间的紧密集成成为可能。SoC 设计师现在可以选择一个标准指令集,该指令集带有大量支持软件,而不必签署合同。
他们仍然需要在设计早期选择指令集,但不必选择一个公司并签署合同。他们可以自己设计一个 RISC-V 核心,可以从多个销售 RISC-V IP 块的公司购买一个,或者可以下载由其他人开发的免费开源 RISC-V IP 块。最后一种情况类似于开源软件,提供网页浏览器、编译器、操作系统等,志愿者为用户维护并免费提供下载和使用。
作为额外的好处,开放指令集的特性改善了小公司提供 RISC-V 技术的商业案例,因为客户不必担心拥有自己独特指令集的公司的长期生存能力。
RISC-V 对 DSA 另一个吸引力在于,指令集的重要性不如通用处理器那么高。如果 DSA 使用更高层次的抽象编程,比如 DAG 或并行模式(例如 Halide 和 TensorFlow),那么在指令集层面上的工作就会减少。此外,在一个通过添加 DSA 来实现性能和能耗进步的世界中,二进制兼容性可能不再像过去那样重要。
在撰写本文时,开放 RISC-V 指令集的未来看起来充满希望。(我们希望能窥探未来,了解 RISC-V 从现在到本书下一版的状态!)
7.9 Putting It All Together: CPUs Versus GPUs Versus DNN Accelerators
我们现在使用深度神经网络(DNN)领域来比较本章中加速器的成本性能。我们首先对 TPU 与标准 CPU 和 GPU 进行全面比较,然后再简要比较 Catapult 和 Pixel Visual Core。图 7.41 显示了我们在此比较中使用的六个基准。这些基准包含了第 7.3 节中三种类型 DNN 的每种类型的两个示例。这六个基准代表了 2016 年 Google 数据中心中 TPU 推理工作负载的 95%。它们通常用 TensorFlow 编写,令人惊讶的是,它们的代码非常简短:仅有 100 到 1500 行代码。它们是运行在主机服务器上的更大应用程序中的小部分,而这些应用程序的 C++ 代码可能多达数千到数百万行。应用程序通常是面向用户的,这导致了严格的响应时间限制,正如我们将看到的那样。图 7.42 和 7.43 显示了正在比较的芯片和服务器。它们是与 TPU 部署同时在 Google 数据中心部署的服务器级计算机。为了在 Google 数据中心部署,它们至少必须检查内部内存错误,这排除了某些选择,例如 Nvidia Maxwell GPU。为了让 Google 购买和部署这些机器,这些机器必须合理配置,而不是仅仅为了赢得基准测试而组装的笨拙人工制品。传统的 CPU 服务器由一款 18 核、双插槽的 Intel Haswell 处理器代表。这个平台也是 GPU 或 TPU 的主机服务器。

图 7.41 显示了六个 DNN 应用程序(每种 DNN 类型两个),它们代表了 TPU 工作负载的 95%。这 10 列包括:DNN 名称、代码行数、DNN 中的层类型及数量(FC 为全连接层;Conv 为卷积层;Element 为 LSTM 的逐元素操作,见第 7.3 节;Pool 为池化层,是一个降维阶段,用平均值或最大值替代一组元素);权重数量;TPU 的运算强度;以及 2016 年 TPU 应用程序的受欢迎程度。由于批量大小的不同,TPU、CPU 和 GPU 之间的运算强度各不相同。TPU 可以拥有更大的批量大小,同时仍能保持在响应时间限制之内。一个 DNN 是 RankBrain(Clark, 2015),一个 LSTM 是 GNM Translate(Wu et al., 2016),一个 CNN 是 DeepMind AlphaGo(Silver et al., 2016;Jouppi, 2016)。

Haswell 是采用 Intel 22 纳米工艺制造的。CPU 和 GPU 都是非常大的芯片:约 600 mm²!GPU 加速器是 Nvidia K80。每个 K80 卡包含两个芯片,并且在内部内存和 DRAM 上提供 SECDED。Nvidia 表示(Nvidia, 2016),K80 加速器通过使用更少、更强大的服务器显著降低了数据中心的成本,提供了应用程序性能。2015 年,DNN 研究人员频繁使用 K80,这也是它们在 Google 部署的时间。请注意,K80 也被 Amazon Web Services 和 Microsoft Azure 在 2016 年底选择用于新的基于云的 GPU。由于每个经过基准测试的服务器的芯片数量在 2 到 8 之间变化,以下图表显示的是每个芯片标准化后的结果,除了图 7.50,它比较了整个服务器的性能/瓦特。
性能:Roofline模型、响应时间和吞吐量
为了说明在三种处理器上六个基准测试的性能,我们借鉴了第四章中的Roofline性能模型。为了将Roofline模型应用于TPU,当深度神经网络(DNN)应用被量化时,我们首先将浮点运算替换为整数乘加运算。由于权重通常不适合DNN应用的片上内存,第二个变化是重新定义操作强度为每读取字节权重的整数运算数(见图7.41)。
图7.44 TPU Roofline。其峰值点位于右侧,达到每字节权重内存1350次乘加运算。CNN1的性能远低于其Roofline,相比于其他DNN,CNN1花费了大约三分之一的时间等待权重加载到矩阵单元中,并且由于CNN中某些层的特征深度较浅,导致矩阵单元中只有一半的元素持有有效值(Jouppi等,2017)。
图7.44展示了在对数坐标系中单个TPU的Roofline模型。TPU的Roofline模型具有一条较长的“倾斜”部分,在该部分,操作强度意味着性能受到内存带宽的限制,而非峰值计算能力。六个应用中有五个正好触碰到上限:多层感知机(MLPs)和长短期记忆网络(LSTMs)受内存限制,而卷积神经网络(CNNs)受计算限制。唯一未触碰上限的DNN是CNN1。尽管CNN具有非常高的操作强度,CNN1的运行速度仅为每秒14.1万亿次运算(TOPS),而CNN0则以令人满意的86 TOPS运行。
图7.45 限制神经网络工作负载TPU性能的因素,基于硬件性能计数器。第1、4、5和6行总和为100%,基于矩阵单元的活动测量。第2和3行进一步细分了在活动周期中,矩阵单元中64K权重中持有有效权重的比例。我们的计数器无法准确解释第6行中矩阵单元空闲时的时间;第7和8行显示了两种可能的原因,包括RAW管道危害和PCIe输入停顿。第9行(TOPS)基于生产代码的测量,而其他行基于性能计数器的测量,因此它们并不完全一致。此处未包括主机服务器的开销。MLP和LSTM受内存带宽限制,但CNN则不受此限制。CNN1的结果在文中进行了说明。
对于想深入了解CNN1的读者,图7.45利用性能计数器提供了TPU利用率的部分视图。TPU在执行CNN1的矩阵运算时,花费的周期不到一半(第7列,第1行)。在每一个活跃周期中,只有约一半的65,536个乘加运算单元(MACs)持有有效权重,因为CNN1中的某些层具有较浅的特征深度。大约35%的周期用于等待将权重从内存加载到矩阵单元,这发生在四个完全连接层上,这些层的操作强度仅为32。这留下了大约19%的周期未被矩阵相关计数器解释。由于TPU的重叠执行,我们无法准确计算这些周期,但我们可以看到,23%的周期由于数据依赖性在流水线中停顿,1%的周期因通过PCIe总线的输入停顿而浪费。

图7.46和7.47展示了Haswell和K80的Roofline模型。这六个神经网络(NN)应用的表现通常比图7.44中的TPU更低于其上限。响应时间限制是原因之一。许多这些DNN应用是面向最终用户的服务的一部分。研究表明,响应时间的微小增加会导致客户减少对服务的使用(见第六章)。因此,尽管训练可能没有严格的响应时间截止日期,但推理通常有。这就是说,推理关注吞吐量的同时,也需保持延迟约束。

图7.48说明了应用开发者要求的在Haswell和K80上为MLP0设定的99百分位响应时间限制7毫秒的影响。(每秒推理次数和7毫秒延迟包括服务器主机时间以及加速器时间。)如果响应时间限制放宽,它们分别可以以42%和37%的最高吞吐量运行MLP0。因此,尽管CPU和GPU的潜在吞吐量可能更高,但如果不能满足响应时间限制,这部分性能将被浪费。这些约束对TPU也有影响,但在图7.48中,TPU以80%的利用率运行,接近其最高的MLP0吞吐量。与CPU和GPU相比,单线程TPU没有第7.1节中讨论的复杂微架构特性,这些特性消耗晶体管和能量以改善平均情况,而不改善99百分位的情况。

图7.49给出了每个芯片的相对推理性能的总结,包括两个加速器的主机服务器开销。请记住,当架构师不知道将要运行的程序的实际组合时,会使用几何平均值。然而,对于此比较,我们确实知道组合(见图7.41)。图7.49最后一列使用实际组合的加权平均值显示,GPU的速度是CPU的最多1.9倍,TPU的速度是CPU的29.2倍,因此TPU的速度是GPU的15.3倍。
成本性能、总拥有成本(TCO)和性能/瓦特
在成千上万地购买计算机时,成本性能优于一般性能。在数据中心中,最佳的成本指标是总拥有成本(TCO)。谷歌实际支付的数千个芯片的价格取决于相关公司之间的谈判。出于商业原因,谷歌无法公布这样的价格信息或可能使其被推断的数据。然而,功耗与TCO相关,谷歌可以公布每台服务器的功率,因此我们使用性能/瓦特作为性能/TCO的代理。在本节中,我们比较的是服务器(图7.43),而不是单个芯片(图7.42)。

图7.50显示了K80 GPU和TPU相对于Haswell CPU的加权平均性能/瓦特。我们提供了两种不同的性能/瓦特计算方式。第一种(“总计”)在计算GPU和TPU的性能/瓦特时,包括主机CPU服务器的功耗。第二种(“增量”)在计算GPU和TPU的总功耗之前,从总功耗中减去主机CPU服务器的功耗。
对于总性能/瓦特,K80服务器的性能是Haswell的2.1倍。对于增量性能/瓦特,当省略Haswell功耗时,K80服务器的性能是2.9倍。TPU服务器的总性能/瓦特比Haswell高出34倍,这使得TPU服务器的性能/瓦特是K80服务器的16倍。相对的增量性能/瓦特(这是谷歌定制ASIC的依据)对于TPU是83倍,这使得TPU的性能/瓦特是GPU的29倍。
评估Catapult和Pixel Visual Core,Catapult V1以2.3倍的速度运行CNN,速度相当于一台2.1 GHz、16核的双插槽服务器(Ovtcharov等,2015a)。使用下一代FPGA(14纳米Arria 10),性能提高了7倍,经过更仔细的布线规划和处理单元的扩展,可能会提高到17倍(Ovtcharov等,2015b)。在这两种情况下,Catapult的功耗增加不到1.2倍。虽然这是不同的比较,但TPU运行其CNN的速度是某些更快服务器的40到70倍(参见图7.42、7.43和7.49)。
由于Pixel Visual Core和TPU都是由谷歌制造的,值得庆幸的是,我们可以直接比较CNN1的性能,这是一个常见的深度神经网络,尽管它需要从TensorFlow转换。它的批量大小为1,而不是TPU中的32。TPU以大约是Pixel Visual Core的50倍的速度运行CNN1,这使得Pixel Visual Core的速度大约是GPU的一半,稍快于Haswell。CNN1的增量性能/瓦特将Pixel Visual Core的性能提高到TPU的约一半,GPU的25倍,以及CPU的100倍。由于Intel Crest旨在用于训练而非推理,因此即使可以进行测量,将其包括在本节中也是不公平的。
7.10 Fallacies and Pitfalls
在早期的数字信号处理器(DSA)和深度神经网络(DNN)阶段,误解层出不穷。
**误解**:设计一款定制芯片的成本为1亿美元。

图7.51展示了一篇文章中的图表,揭穿了广为流传的1亿美元神话,实际上设计成本“仅为”5000万美元,其中大部分成本是工资(Olofsson, 2011)。请注意,作者的估计是针对包含DSA定义中所省略的特性的复杂处理器,因此即使开发过程没有改善,你也可以预计DSA设计的成本会更低。
那么,六年后我们为何更加乐观,尤其是在小型工艺技术的掩模成本甚至更高的情况下呢?
首先,软件是成本最大的部分,占近三分之一。使用领域特定语言编写的应用程序的可用性使得编译器可以完成将应用程序移植到DSA的大部分工作,正如我们在TPU和Pixel Visual Core中所看到的那样。开放的RISC-V指令集也将有助于降低系统软件的获取成本,并削减大量的知识产权成本。
通过多个项目共享单一掩模,可以节省掩模和制造成本。只要有小型芯片,令人惊讶的是,以3万美元的价格,任何人都可以获得100个未经测试的28纳米TSMC技术零件(Patterson和Nikoli!c,2015)。
或许最大的变化在于硬件工程,硬件工程占成本的四分之一以上。硬件工程师已经开始效仿软件同事,采用敏捷开发。传统的硬件流程不仅在设计需求、架构、逻辑设计、布局、验证等各个阶段有不同的流程,而且执行每个阶段的人员也有不同的职位名称。这个过程在计划、文档和排期方面较为繁重,部分原因是每个阶段的人事变动。
软件曾经也遵循这种“瀑布”模型,但项目常常超期、超预算甚至被取消,这导致了一种截然不同的方法。2001年的敏捷宣言基本上表示,与其使用传统的计划和文档方式,能够定期向客户展示一个不完整但可工作的原型的小团队更有可能按时按预算交付有用的软件。
现在,小型硬件团队也进行敏捷迭代(Lee等,2016)。为了减轻芯片制造的长期延迟,工程师们利用FPGA进行一些迭代,因为现代设计系统可以从单一设计生成FPGA的EDIF和芯片布局。FPGA原型的运行速度比芯片慢10到20倍,但这仍然比模拟器快得多。他们还进行“磁带输入”迭代,即对工作但不完整的原型进行所有的磁带输出工作,但不需要支付制造芯片的成本。
除了改进的开发流程,还有更现代的硬件设计语言来支持这些流程(Bachrach等,2012),以及从高级领域特定语言自动生成硬件的进展(Canis等,2013;Huang等,2016;Prabhakar等,2016)。可以免费下载和修改的开源核心也应该降低硬件设计的成本。
**陷阱**:性能计数器作为DSA硬件的附加思考而被添加。
TPU有106个性能计数器,设计者希望有更多(见图7.45)。DSA的存在理由是性能,而在它们的演变过程中,现在还为时尚早,无法充分了解其运行情况。
**谬论**:建筑师正在解决正确的DNN任务。
建筑界正关注深度学习:2016年ISCA会议上15%的论文集中于DNN的硬件加速器!然而,所有九篇论文都研究了CNN,只有两篇提到其他DNN。CNN比MLP更复杂,并且在DNN比赛中占据主导地位(Russakovsky等,2015),这可能解释了它们的吸引力,但它们仅占谷歌数据中心神经网络工作负载的约5%。因此,尝试以同样的热情加速MLP和LSTM似乎更为明智。
**谬论**:对于DNN硬件,每秒推理次数(IPS)是一个合理的总体性能指标。
IPS不适合作为DNN硬件的单一总体性能摘要,因为它仅仅是应用中典型推理复杂度的倒数(例如,神经网络层的数量、大小和类型)。例如,TPU在360,000 IPS下运行4层的MLP1,但89层的CNN1仅为4700 IPS;因此TPU的IPS变化达到75倍!因此,将IPS作为唯一速度总结对神经网络加速器的误导性远高于MIPS或FLOPS对传统处理器的误导性,因此IPS更应被贬低。为了更好地比较DNN机器,我们需要一个高层次编写的基准测试套件,以便将其移植到各种DNN架构。Fathom是这样一个基准测试套件的一个有前景的新尝试(Adolf等,2016)。
**陷阱**:在设计DSA时对架构历史的无知。
在通用计算中未能成功的想法可能非常适合DSA,因此了解历史的架构师可能会拥有竞争优势。对于TPU,有三个重要的架构特征可以追溯到1980年代初期:脉冲阵列(Kung和Leiserson,1980)、解耦访问/执行(Smith,1982b)和CISC指令(Patterson和Ditzel,1980)。第一个特征减少了大型矩阵乘法单元的面积和功耗,第二个特征在矩阵乘法单元操作期间同时提取权重,而第三个特征则更好地利用了用于传送指令的PCIe总线的有限带宽。我们建议阅读本书每一章末尾的历史视角部分,以发现可能为您设计的DSA增光添彩的珍贵思想。
7.11 Concluding Remarks
在本章中,我们看到了一些商业实例,展示了从改善通用计算机以使所有程序受益的传统目标,向利用数据特定架构(DSA)加速部分程序的转变。
Catapult 的两个版本通过设计一个小型低功耗 FPGA 板来保持数据中心的同质性,该 FPGA 板可以装入服务器。希望 FPGA 的灵活性能够使 Catapult 对许多当前应用程序以及部署后出现的新应用程序都能派上用场。Catapult 在搜索排序和卷积神经网络(CNN)的运行速度上超越了 GPU,为排名相较于 CPU 提供了 1.5 到 1.75 倍的性能/总拥有成本提升。
TPU 项目最初是以 FPGA 开始的,但在设计者得出当时的 FPGA 与 GPU 相比性能不具竞争力的结论后放弃了 FPGA。他们还认为 TPU 的功耗将远低于 GPU,同时速度相同或更快,这使得 TPU 有可能在性能上优于 FPGA 和 GPU。最终,TPU 并没有打破谷歌数据中心的同质性,因为其数据中心中的一些服务器已经配备了 GPU。TPU 基本上是跟随 GPU 的脚步,仅仅是另一种类型的加速器。
TPU 的非重复工程成本可能远高于 Catapult,但其回报也更大:ASIC 的性能和性能/瓦特都远高于 FPGA。风险在于 TPU 只适用于 DNN 推理,但正如我们所提到的,DNN 是一个有吸引力的目标,因为它们可以用于许多应用程序。在 2013 年,谷歌的管理层抱着信心,认为 2015 年及以后对 DNN 的需求将证明投资 TPU 的合理性。
Catapult 和 TPU 的确定性执行模型更适合用户应用程序的响应时间截止,而 CPU 和 GPU 的时间变化优化(如缓存、乱序执行、多线程、并行处理、预取等)则更多地帮助平均吞吐量而非延迟。缺乏此类特性有助于解释为什么尽管 TPU 拥有众多 ALU 和大内存,但其相对较小且低功耗。这个成就表明了阿姆达尔定律的一个“丰饶推论”:低利用率的大型廉价资源仍然可以提供高性价比的性能。
总之,TPU 成功用于 DNN 的原因在于其大型矩阵单元;大量软件控制的片上内存;能够运行整个推理模型以减少对主 CPU 的依赖;单线程的确定性执行模型很好地契合了 99 百分位的响应时间限制;足够的灵活性以匹配 2017 年及 2013 年的 DNN;由于省略了通用功能,尽管数据路径和内存较大,仍实现了小巧低功耗的芯片;量化应用程序使用 8 位整数;以及应用程序是使用 TensorFlow 编写的,这使得将它们高效地移植到 DSA 上变得简单,而不是必须重新编写以便在完全不同的硬件上良好运行。
Pixel Visual Core 展示了为 PMD 设计 DSA 的约束,涉及芯片尺寸和功耗。与 TPU 不同,Pixel Visual Core 是一个独立的处理器,能够自行获取指令。尽管主要针对计算机视觉,Pixel Visual Core 在性能/瓦特方面的表现比 K80 GPU 和 Haswell CPU 提高了一个到两个数量级。
对于 Intel Crest 进行评判还为时尚早,尽管其被 Intel CEO 热情宣布,标志着计算领域的转变。
### 架构复兴
在过去十年中,架构研究人员一直在发布基于有限基准的创新,声称通用处理器的性能提升不超过 10%。而如今,许多公司报告的专用硬件(DSA)产品的性能提升却达到了 10 倍或更多。
我们认为这表明该领域正在经历转变,并预计在下一个十年中将看到架构创新的复兴,原因包括:
- Dennard 缩放和摩尔定律的历史终结,这意味着提升成本-能耗-性能的需求将需要计算机架构的创新;
- Agile 硬件开发和新的硬件设计语言带来的生产力进步,这些语言充分利用了现代编程语言的进展;
- 由于免费和开放的指令集、开源 IP 模块以及商业 IP 模块(目前大多数 DSA 都基于这些)而降低的硬件开发成本;
- 上述生产力和开发成本的提升意味着研究人员可以通过在 FPGA 或定制芯片中构建自己的想法来证明它们,而不是试图用模拟器说服怀疑者;
- DSA 的潜在优势及其与领域特定编程语言的协同效应。
我们相信,许多架构研究人员将构建出更高水平的 DSA,超越本章讨论的内容。我们迫不及待想要看到下一版书籍发布时计算机架构的世界会是什么样子!