Chapter 3 Hardware and its Habits

过早抽象是一切罪恶的根源。   

成千上万的人

大多数人直觉地认为,在系统之间传递消息比在一个系统内部执行简单的计算要昂贵得多。但同样,单共享内存系统中线程之间的通信也可能非常昂贵。因此,本章探讨了在共享内存系统中同步和通信的成本。这几页内容最多只能触及共享内存并行硬件设计的表面;希望了解更多细节的读者最好从亨尼斯和帕特森的经典著作的最新版本开始阅读[HP17,HP95]。              

3.1概述

机械同感:硬件和软件协同工作。   

马丁·汤普森

对计算机系统规格表的粗心阅读可能会让人相信CPU性能是一条清晰赛道上的赛跑,如图3.1所示,比赛总是由跑得最快的人获胜。  

尽管有一些CPU限制的基准测试接近图3.1中显示的理想情况   典型的程序更像是一项障碍赛跑,而不是一条跑道。这是因为过去几十年里,由于摩尔定律的推动,CPU的内部架构发生了巨大变化。这些变化将在接下来的部分中详细描述。           

3.1.1流水线CPU

20世纪80年代,典型的微处理器获取一条指令,解码并执行它,通常至少需要三个时钟周期来完成一条指令,甚至在开始下一条指令之前。相比之下,20世纪90年代末和21世纪初的CPU可以同时执行多条指令,利用流水线、超标量技术和乱序执行技术。                  

                   

    

指令和数据处理;推测执行等[HP17,HP11],以优化指令和数据通过CPU的流动。某些核心拥有多个硬件线程,这被称为同时多线程(SMT)或超线程(HT)[ Fen73],每个线程在软件看来都是一个独立的CPU,至少从功能角度来看是这样。这些现代硬件特性可以显著提升性能,如图3.2所示。                        

要实现具有长流水线的CPU的全性能,需要程序中高度可预测的控制流。合适的控制流可以通过主要执行紧密循环的程序来提供,例如对大型矩阵或向量进行算术运算。这样,CPU几乎可以在所有情况下正确预测循环结束时会执行分支,从而使流水线保持满载状态,CPU能够以全速运行。                        

然而,分支预测并非总是那么简单。例如,考虑一个包含许多循环的程序,每个循环迭代次数虽少但随机。再比如,考虑一个传统的面向对象程序,其中有许多虚拟对象可以引用多个不同的真实对象,这些对象频繁调用成员函数时实现各不相同,导致通过指针进行大量调用。在这种情况下,CPU难以甚至无法预测下一个分支会指向何处。因此,CPU必须等待执行足够长的时间以确定分支指向的位置,或者只能猜测并使用推测执行继续前进。尽管对于控制流可预测的程序,猜测效果非常好,但对于不可预测的分支(如二分查找中的分支),猜测结果经常出错。错误的猜测代价高昂,因为CPU必须丢弃相应分支之后的所有推测执行指令,导致流水线清空。如果流水线清空过于频繁,会大幅降低整体性能,如图3.3所示。                                                                                     

这种情况在超线程(或SMT,如果你喜欢的话)中变得更加普遍,尤其是在具有推测执行功能的流水线超标量乱序CPU上。在这种越来越常见的情况下,共享同一核心的所有硬件线程也共享该核心的资源,包括寄存器、缓存、执行单元等。指令通常被解码为微操作,并使用共享的执行单元。            


单元和数百个硬件寄存器通常由微操作调度器协调。图3.4给出了这样一个双线程核心的粗略示意图, 教科书和学术论文中提供了更精确(因而更复杂)的图表。 因此,一个硬件线程的执行可以而且经常会被共享该核心的其他硬件线程的操作所干扰。     

即使只有一个硬件线程处于活动状态(例如,在旧式学校CPU设计中只有一个线程),反直觉的结果也相当常见。执行单元通常具有重叠的功能,因此CPU选择的执行单元可能会导致管道停滞,因为后续指令会争夺该执行单元。理论上,这种竞争是可以避免的,但在实际操作中,CPU必须迅速做出选择,而没有先见之明的好处。特别是,在紧致循环中添加一条指令有时实际上可以加快执行速度。                

不幸的是,管道冲洗和共享资源竞争并不是现代CPU必须运行的障碍赛程中的唯一危险。下一节将介绍引用内存的危险。                  

3.1.2内存引用

在20世纪80年代,微处理器从内存中加载一个值的时间通常比执行一条指令要少。近年来,微处理器可能在访问内存所需的时间内执行数百甚至数千条指令。这种差异是由于摩尔定律使得CPU性能的增长速度远超内存延迟的减少速度,部分原因是内存容量的增长速度。例如,典型的20世纪70年代小型计算机可能只有4KB(没错,是千字节,而不是兆字节,更不用说吉字节或太字节)的主存,并且单周期访问。 当今的CP U设计者仍然可以构建一个4 KB的单周期访问内存,即使是在多GHz时钟频率的系统上。事实上,他们经常构建这样的内存,但现在称其为“0级缓存”,而且它们可以比4 KB大一些。                                                                                       

尽管现代微处理器上的大缓存可以显著减少内存访问延迟,但这些缓存需要高度可预测的数据访问模式才能成功隐藏这些延迟。不幸的是,常见的操作如遍历链表具有极其不可预测的内存访问模式——毕竟,如果模式是可预测的,我们这些软件类型岂不是就不用费心去处理指针了?因此,如图3.5所示 ,内存引用通常会给现代cpu带来严重的障碍。 

到目前为止,我们只考虑了在单线程代码执行过程中可能出现的障碍。多线程为CPU带来了额外的障碍,如下几节所述。    

3.1.3原子操作

其中一个障碍是原子操作。问题在于,原子操作的概念与CPU流水线逐件处理的操作理念相冲突。值得称赞的是,现代CPU使用了多种极其巧妙的技巧,使得这些操作看起来像是原子性的,尽管实际上它们是逐件执行的。一种常见的技巧是识别出所有包含待原子操作数据的缓存行,确保这些缓存行由执行原子操作的CPU拥有,然后在确保这些缓存行仍由该CPU拥有的情况下进行原子操作。由于所有数据都是私有的,其他CPU无法干扰原子操作,尽管CPU流水线的逐件处理特性。可想而知,这种技巧可能需要延迟甚至清空流水线,以完成允许特定原子操作正确完成的设置操作。                                                    

相比之下,在执行非原子操作时,CPU可以加载缓存行中的值,并将结果放入存储缓冲区,而无需等待缓存行所有者。尽管有许多硬件优化        

图3.6:CPU遇到原子操作 

这有时会隐藏缓存延迟,对性能的影响往往如图3.6所示。    

不幸的是,原子操作通常只适用于数据中的单个元素。由于许多并行算法要求在多个数据元素更新之间保持顺序约束,大多数CPU提供了内存屏障。这些内存屏障也作为性能消耗障碍,如下一节所述。                                

3.1.4内存障碍   

将在第15章和附录C中更详细地讨论内存屏障。与此同时,请考虑以下基于锁的简单临界区:    

spin_lock(&mylock);a= a+1;        

spin_unlock(&mylock);

如果CPU不按所示顺序执行这些语句,那么变量“a”会在没有“mylock”保护的情况下递增,这显然违背了获取它的初衷。为了防止这种破坏性的重排序,锁定原语包含显式或隐式的内存屏障。由于这些内存屏障的全部目的是防止CPU为了提高性能而进行的重排序,因此内存屏障几乎总是会降低性能,如图3.7所示。                           

与原子操作一样,CPU设计者一直在努力减少内存屏障开销,并取得了实质性的进展。            

3.1.5热阻塞   

一个越来越常见的令人沮丧的经历是,仔细地微优化一个关键的代码路径,大大减少了该代码路径消耗的时钟周期数,结果却发现该代码消耗的实际时间实际上增加了。        

欢迎来到现代热节流。

如果你通过更有效地利用CPU的功能单元来减少时钟周期的数量,那么你将增加该CPU的功耗。这反过来又会增加该CPU散发的热量。如果这种散热超过了冷却系统的容量,系统将对CPU进行热管理,例如通过降低其时钟频率,就像图3.8中雪企鹅所描绘的那样。                     

如果性能是关键,正确的解决方法就是改进冷却,这是严肃玩家和超频爱好者的最爱。但是,如果你无法修改计算机的冷却系统,可能是因为你从云服务提供商那里租用了它,那么你就需要采取其他优化方法。例如,你可能需要应用算法优化,而不是依赖硬件的微优化。或者,你可以并行化你的代码,将工作(以及热量)分散到多个CPU核心上。                                   

3.1.6缓存未命中 

额外的多线程障碍是“缓存未命中”。如前所述,现代CPU配备了大容量缓存,以减少因高内存延迟而产生的性能损失。然而,对于频繁在不同CPU之间共享的变量而言,这些缓存实际上适得其反。这是因为当某个CPU希望修改某个变量时,很可能其他CPU最近已经对该变量进行了修改。在这种情况下,该变量可能存在于另一个CPU的缓存中,但不在当前CPU的缓存中,从而导致昂贵的缓存未命中(详见附录C.1)。这种缓存未命中是CPU性能的主要障碍,如图3.9所示。                        

图3.10:CPU等待I/O完成 

3.1.7I/O操作   

缓存未命中可以视为一种CPU到CPUI/O的操作,因此是可用的最便宜的I/O操作之一。涉及网络、大容量存储或(更糟糕的是)人类的I/O操作比前几节提到的内部障碍要困难得多,如图3.10所示。                            

这是共享内存与分布式系统并行性之间的区别之一:共享内存并行程序通常不会遇到比缓存未命中更严重的障碍,而分布式并行程序则会因较大的网络通信延迟而受到影响。在这两种情况下,相关的延迟可以视为通信成本——这种成本在顺序程序中是不存在的。因此,通信开销与实际工作量之间的比例是一个关键的设计参数。并行硬件设计的主要目标之一是根据需要降低这一比例,以实现相关性能和可扩展性的目标。同样地,正如将在第六章中看到的,并行软件设计的主要目标之一是减少诸如通信缓存未命中等昂贵操作的频率。                                             

当然,说某项操作是障碍是一回事,而证明该操作是重大障碍则是另一回事。这种区别将在下面的章节中讨论。        

3.2管理费用

不要在不了解材料的情况下设计桥梁,也不要以不了解底层硬件为由设计低级软件。            

未知的

本节介绍前一节中列出的性能障碍的实际开销,但首先有必要对硬件系统架构有一个大致的了解,这是下一节的主题。         

3.2.1硬件系统架构

图3.11  显示了一个八核计算机系统的粗略示意图。每个芯片都有一对CPU核心,每个核心都有自己的缓存,以及一个互连,允许这对CPU相互通信。系统互连使得四个芯片能够互相通信,并与主内存通信。                

数据以“缓存行”的单位通过该系统移动,这些缓存行是固定大小的对齐内存块,通常大小在3²到256字节之间。当CPU从内存中加载一个变量到其寄存器时,必须首先将包含该变量的缓存行加载到其缓存中。同样地,当CPU将一个寄存器中的值存储到内存中时,也必须将包含该变量的缓存行加载到其缓存中,并且还必须确保没有其他CPU拥有该缓存行的副本。       

例如,如果CPU 0要写入一个缓存行位于CPU 7缓存中的变量,则可能会出现以下过于简化的事件序列:  

1. CPU 0检查其本地缓存,没有找到该缓存行。因此,它将写入记录在其存储缓冲区中。         

2.请求此缓存行被转发到CPU 0和1的互连,检查CPU1的本地缓存,但没有找到该缓存行。      

3.此请求被转发到系统互连,该互连与另外三个芯片进行检查,得知缓存内由包含CPU 6和7的芯片持有。      

4.此请求被转发到CPU6和7的互连,该互连检查两个CPU的缓存,在CPU 7的缓存中找到值。               

5. CPU 7将缓存行转发到其互连,并且从其缓存中清除该缓存行。     

6. CPU 6和7的互连将缓存行转发到系统互连。  

7.系统互连将缓存行转发至CPU0的and1互连。                    

8. CPU 0和1的互连将缓存行转发到CPU0的缓存。   

9. CPU 0现在可以完成写入,更新新到达的缓存行的相关端口离子,从存储缓冲区中先前记录的值。    

这个简化的序列仅仅是缓存一致性协议[HP95,CSG99,MHS12,SHW11]这一学科的开端,更多细节请参见附录C。从事件序列中可以看出,由a触发CAS操作,一条指令可引起大量的协议流量,这会显著降低并行程序的性能。            

幸运的是,如果某个变量在一段时间内频繁读取但从未更新,该变量可以复制到所有CPU的缓存中。这种复制使得所有CPU都能以极快的速度访问这个主要读取的变量。第9章介绍了同步机制,充分利用了这一重要的硬件读取优化。                            

3.2.2手术费用

一些对并行程序很重要的常见操作的开销如表3.1所示。   该系统的时钟周期四舍五入为0.5纳秒。尽管现代微处理器通常能够在每个时钟周期内完成多条指令的执行,但操作的成本仍然在第三列“比率”中以时钟周期为单位进行了标准化。关于这张表,首先需要注意的是许多比率的值非常大。           

相同的CPU比较交换(CAS)操作大约需要十秒的时间,这一时间是时钟周期的十倍多。CAS是一种原子操作,其中硬件会将指定内存位置的内容与指定的“旧”值进行比较,如果它们相等,则存储一个指定的“新”值,此时CAS操作成功。如果不相等,内存位置保持其(意外的)值,CAS操作失败。该操作具有原子性,因为硬件保证在比较和存储之间不会改变内存位置。CAS功能由x86中的锁;cmpxchg指令提供。                                                         

“相同CPU”前缀表示当前执行CAS操作的CPU也是最后一个访问该变量的CPU,因此对应的缓存行已经存在于该CPU的缓存中。同样地,“相同CPU”锁操作(由一次获取和释放组成的“往返”对)耗时超过十五纳秒,或超过三十个时钟周期。锁操作比CAS更昂贵,因为它需要对锁数据结构进行两次原子操作,一次用于获取,另一次用于释放。                           

涉及共享单个核心的硬件线程之间交互的内核操作的成本与asm-eCPU操作的成本大致相同。这并不令人惊讶,因为这两个硬件线程还共享完整的缓存层次结构。             

对于盲CAS,软件在不查看内存位置的情况下指定旧值。当尝试获取锁时,这种方法是合适的。     

表3.1:CPU 0视图:8插槽系统上使用Intel Xeon Platinum 81 76个CPU的同步机制@2.10 GHz        

操作

成本(ns) 

比率(成本/时钟) 

中央处理器

时钟周期

0.5

1.0

相同CPU的CAS 

锁上

7.0 15.4 

14.6 32.3 

0

在核心

盲法CAS CAS 

7.2 18.0 

15.2 37.7 

224

离线

盲法CAS CAS 

47.5 101.9 

99.8 214.0 

1–27225–251 

离线

盲法CAS CAS 

148.8 442.9 

312.5 930. 1  

28–111252–335 

交叉互连盲CAS  

CAS

336.6 944.8 

706.8 1,984.2 

112–223336–447 

系统外

Comms Fabric全球通信  

5,000

195,000,000

10,500

409,500,000

如果解锁状态用零表示,锁定状态用值一表示,那么对锁执行指定旧值为零、新值为一的CAS操作,如果锁尚未被持有,则该操作将成功获取锁。关键在于,内存位置的访问仅限于一次,即CAS操作本身。                

相比之下,CAS操作的旧值是从某个早期加载中获取的。例如,为了实现原子增量操作,会先加载该位置的当前值,然后将该值加一以生成新值。在CAS操作中,实际加载的值会被指定为旧值,而加一后的值则作为新值。如果在这次加载和CAS之间没有改变该值,这将导致内存位置的增量。然而,如果该值确实发生了变化,则旧值不会匹配,从而导致比较失败。关键在于,现在内存位置有两次访问,一次是加载,另一次是CAS。                                                

因此,核心盲CAS仅消耗约7纳秒,而核心CAS则消耗约18纳秒也就不足为奇了。核心盲CAS的额外负载并非免费获得。也就是说,这些操作的开销分别类似于相同CPU的CAS和锁。 

表3.2:配备Intel Xeon Platinum 8176 CPU的8插槽系统的缓存几何结构@2.10 GHz    

等级

范围

线条大小

集锦

方式

尺寸

L1

核心

64

64

8

32K

L2

核心

64

1024

16

1024K

L3

插座

64

57,344

11

39,424K

盲CAS涉及同一插槽内不同核心的CPU,耗时近五十纳秒,或接近一百个时钟周期。用于此缓存未命中测量的代码会在一对CPU之间来回传递缓存行,因此这种缓存未命中的满足不是来自内存,而是来自另一台CPU的缓存。非盲CAS操作,如前所述,必须查看变量的旧值并存储新值,耗时超过一百纳秒,或超过两百个时钟周期。仔细思考一下。在一秒钟内完成一次CAS操作的时间,CPU可以执行超过两百条正常指令。这不仅展示了细粒度锁定的局限性,也揭示了任何依赖于细粒度全局一致性的同步机制的局限性。                                                           

如果这对CPU位于不同的插槽上,操作的代价就大得多。盲CAS操作消耗近150纳秒,或超过300个时钟周期。正常CAS操作消耗超过400纳秒,或近1000个时钟周期。                  

更糟糕的是,并非所有社会契约对都是一样的。这个特定系统似乎是由四个插槽组件组成的,当CPU位于不同组件时会产生额外的延迟惩罚。在这种情况下,盲CAS操作需要超过三百纳秒,即超过七百个时钟周期。而一个CAS操作几乎需要整整一微秒,即接近两千个时钟周期。 

   

不幸的是,内核和内核间通信的高速度并非免费。首先,在给定的核心中只有两个CPU,在给定的套接字中只有56个,而整个系统中有448个。其次,如表3.2所示,与theon-socket缓存相比,theon-core缓存要小得多,而后者又比该系统配置的1.4 TB内存要小。第三,再次参考图示,缓存被组织成一个硬件表,每个桶中的项目数量有限。例如,L3缓存(“大小”)的原始大小接近40MB,但每个桶(“行”)只能容纳11块内存(“路”),每块最多64字节(“行大小”)。这意味着只需12字节的内存(当然是在精心选择的地址处)就能溢出这40MB的缓存。另一方面,同样谨慎地选择地址可能会充分利用整个40MB的内存。       

参考的空间局部性显然非常重要,数据在内存中的分布也是如此。   

I/O操作更加昂贵。如“通信结构”一栏所示,高性能(且昂贵!)的通信结构,如InfiniBand或任何专有互连,在端到端往返过程中大约有五微秒的延迟时间,在此期间可能执行了超过一千条指令。基于标准的通信网络通常需要某种协议处理,这进一步增加了延迟。当然,地理距离也会增加延迟,全球光纤传输的速度大约为195毫秒,相当于超过4亿个时钟周期,如“全球通信”一栏所示。                                     

3.2.3硬件优化   

很自然地会问硬件是如何帮助的,答案是“相当多!” 

一种硬件优化是大缓存行。这可以显著提升性能,尤其是在软件按顺序访问内存时。例如,假设一个64字节的缓存行,软件访问64位变量,首次访问仍然会因为光速延迟而较慢(如果其他方面没有问题的话),但随后的七次访问可以非常快。然而,这种优化也有其阴暗面,即虚假共享,当同一缓存行中的不同变量被不同的CPU更新时,会导致较高的缓存未命中率。软件可以利用许多编译器提供的对齐指令来避免虚假共享,添加此类指令是调优并行软件的常见步骤。  

第二个相关的硬件优化是缓存预取,即硬件通过预取后续的缓存行来应对连续访问,从而避免这些后续缓存行的光速延迟。当然,硬件必须使用简单的启发式方法来确定何时预取,而这些启发式方法可能会被许多应用程序中的复杂数据访问模式所误导。幸运的是,一些CPU家族通过提供特殊的预取指令来实现这一点。不幸的是,这些指令在一般情况下的有效性仍存在争议。          

第三项硬件优化是存储缓冲区,它使得即使在存储地址不连续且所需缓存行不在CPU缓存中时,也能快速执行一串存储指令。这项优化的阴暗面是内存乱序,详见第15章。                         

第四项硬件优化是推测执行,这可以让硬件充分利用存储缓冲区,而不会导致内存乱序。然而,这种优化的阴暗面可能是能耗不高效和性能下降,如果推测执行出错并需要回滚重试。更糟糕的是,Spectre和Meltdown的出现[Ho r18]表明,硬件推测还可能引发侧信道攻击,这些攻击可以绕过内存保护硬件,使非特权进程能够读取它们本不应访问的内存。显然,推测执行与云计算的结合需要更多的改进!                                 

第五个硬件优化是大缓存,允许单个CPU在不产生昂贵的缓存未命中的情况下操作更大的数据集。尽管大缓存     

会降低能效和缓存未命中延迟,生产微处理器上不断增长的缓存大小证明了这种优化的力量。     

最后一种硬件优化是读取-主要复制,其中经常被读取但很少被更新的数据存在于所有CPU的缓存中。这种优化使读取-主要数据能够极其高效地访问,是第9章的主题。                     

简而言之,硬件和软件工程师是站在同一边的,他们都试图让计算机运行得更快,尽管物理定律尽了最大努力,如图3.12所示    我们的数据流正竭尽全力超越光速。下一节将讨论一些额外的事情,这些事情取决于最近的研究成果能否顺利转化为实际应用,硬件工程师可能(或不可能)能够做到。本书后续章节将概述软件对这一崇高目标的贡献。                  

3.3硬件免费午餐?   

今天的大麻烦是,有太多的   

人们总是在寻找别人替他们做事。解决我们大多数问题的办法就是每个人都为自己做点什么。                

亨利·福特,更新 

近年来,并发受到了如此多的关注的主要原因是摩尔定律所导致的单线程性能提升(或“免费午餐”[Sut08])的终结,如第12页图2.1所示。本节简要回顾了硬件设计者可能有几种方法可以带来“免费午餐”。 

然而,前一节介绍了一些利用并发的实质性硬件障碍。硬件设计者面临的一个严重的物理限制是光速的有限性。如图3.11所示第34页  光在1.8 GHz时钟周期内,在真空中只能传播大约8厘米的距离。这一距离在5 GHz时钟周期下降至约3厘米。与现代计算机系统的规模相比,这两个距离都相对较小。                    

更糟糕的是,硅中的电波移动速度比真空中光的速度慢3到30倍,而常见的时钟逻辑结构仍然运行     

例如,内存引用可能需要等待本地缓存查找完成,才能将请求传递给系统其余部分。此外,为了将电信号从一个硅片传输到另一个硅片,例如在CPU和主内存之间通信,需要相对低的速度和高功率驱动器。        

                  

尽管如此,仍有一些技术(包括硬件和软件)可以帮助改善情况:  

1. 3D集成,

2.新型材料和工艺,

3.用光代替电,

4.专用加速器,以及

5.现有并行软件。

这些内容在以下各节中进行了描述。

3.3.1 3D集成   

三维集成(3DI)是将非常薄的硅片垂直堆叠在一起的实践。这种做法提供了潜在的好处,但也带来了显著的制造挑战[Kni08]。               

也许3DI最重要的好处是减少了系统中的路径长度,如图3.13所示。用four1.5-centimeter层叠硅片替代了3厘米的硅片,在理论上可以将系统中的最大路径减少一半,同时要记住每一层都非常薄。此外,只要设计和布局得当,长水平电气连接(既慢又耗电)可以被短垂直电气连接所取代,后者不仅更快,而且更节能。               

然而,由于时钟逻辑水平导致的延迟不会因3D集成而减少,且为了使3D集成在生产中仍能兑现其承诺,必须解决重大的制造、测试、电源供应和散热问题。散热问题可能通过基于半导体的方法来解决。         

金刚石是良好的热导体,但却是电绝缘体。因此,要生长出大单晶金刚石已经相当困难,更不用说将其切割成晶圆了。此外,这些技术似乎不太可能实现一些人所习惯的指数级增长。话虽如此,它们可能是通往吉姆·格雷晚年所说的“冒烟的毛球”[Gra02]的必要步骤。             

3.3.2新型材料和工艺

据说斯蒂芬·霍金曾声称,半导体制造者面临两个基本问题:(1)光速有限和(2)物质的原子性质[Gar07]。尽管半导体制造商可能正在接近这些极限,但仍有几条研究和发展路径专注于绕过这些基本限制。           

一种解决物质原子性质的方法是所谓的“高介电常数”材料,这些材料使得较大的设备能够模拟出微小设备的电学特性。这些材料带来了一些严重的制造挑战,但仍然可能帮助将前沿推进得更远一些。另一种更为奇特的方法是在单个电子中存储多个比特,利用给定电子可以存在于多个能级这一事实。这种特定方法是否能在生产中的半导体器件中可靠地实现,还有待观察。                           

另一种提出的变通方法是“量子点”方法,它允许更小的设备尺寸,但仍然处于研究阶段。             

一个挑战是,许多最近的硬件设备级突破需要非常精确地控制哪些原子被放置在哪里[Kel17]。因此,似乎可以肯定的是,无论谁找到一种好方法将原子手工放置在芯片上的数十亿个设备中的每一个上,即使没有其他优势,也将拥有极高的竞争权!       

3.3.3灯光,而非电子管

尽管光速是一个难以突破的极限,但事实是半导体器件的速度受限于电的速度而非光速,因为半导体材料中的电波传播速度仅为真空中光速的3%到30%。在硅器件上使用铜连接是一种提高电速的方法,未来的技术进步可能会进一步接近光速。此外,一些实验已经使用微小的光纤作为芯片内部和之间的互连,基于玻璃中光速超过真空中光速60 %的事实。然而,这种光纤的一个障碍在于电与光之间转换效率低下,导致功耗和散热问题。                                       

也就是说,在物理学领域没有一些基本的进展的情况下,数据流速度的指数级增长将受到真空中光速的实际速度的严格限制。          

3.3.4专用加速器

通用CPU在处理特定问题时,往往会花费大量时间和精力去做与当前问题只有间接关系的工作。例如,在计算一对向量的点积时,通用CPU通常会使用带有循环计数器的循环(可能是展开的)。解码指令,         

增加循环计数器、测试这个计数器并返回到循环顶部,在某种程度上是浪费精力:真正的目标是将两个向量中的对应元素相乘。因此,专门设计用于向量相乘的硬件可以更快地完成任务,同时消耗更少的能量。                         

事实上,这是许多通用微处理器中向量指令存在的动机。因为这些指令同时对多个数据项进行操作,所以它们允许以更少的指令解码和循环开销来计算点积。      

同样,专用硬件可以更高效地进行加密和解密、压缩和解压、编码和解码等众多任务。遗憾的是,这种效率并非免费获得。集成这些专用硬件的计算机系统将包含更多的晶体管,即使在不使用时也会消耗一些电力。软件必须被修改以利用这种专用硬件,同时这些专用硬件必须足够通用,使得高层硬件设计成本能够分摊到足够多的用户身上,从而使专用硬件变得负担得起。由于这些经济因素,迄今为止,专用硬件仅出现在少数应用领域,包括图形处理(GPU)、矢量处理器(MMX、SSE和VMX指令),以及在较小程度上的加密。即使在这些领域中,实现预期的性能提升也并非总是那么容易,例如,由于热限制[Kra17,Lem18,Dow20]。                                                  

与服务器和PC领域不同,智能手机长期以来一直使用各种各样的硬件加速器。这些硬件加速器通常用于媒体解码,以至于高端MP3播放器可能在CPU完全关闭的情况下播放音频几分钟。这些加速器的目的是提高能效,从而延长电池寿命:专用硬件往往比通用CPU计算效率更高。这是第2.2.3节中提到的原则的另一个例子:通用性几乎从来不是免费的。                                     

然而,考虑到摩尔定律引起的单线程性能增加的结束,似乎可以安全地假设特殊目的硬件的种类会增加。       

3.3.5现有并行软件

尽管多核CPU似乎让计算行业大吃一惊,但事实是共享内存并行计算机系统已经商用超过四分之一个世纪了。这足以让重要的并行软件出现,而它确实做到了。并行操作系统非常普遍,同时并行线程库、并行关系数据库管理系统和并行数值软件也十分常见。利用现有的并行软件可以在很大程度上解决我们可能遇到的任何并行软件危机。                  

也许最常见的例子是并行关系数据库管理系统。单线程程序,通常用高级脚本语言编写,同时访问中央关系数据库并不罕见。在这种高度并行的系统中,只有数据库需要直接处理并行性。当这一技巧奏效时,真是妙不可言!    

3.4软件设计影响

一艘船向东驶去,另一艘向西驶去,而同样的微风在吹拂着;    

是帆的集合,而不是风向标指引他们去哪里。      

埃拉·惠勒·威尔科克斯

表3.1中比率的值  至关重要,因为它们限制了给定并行应用程序的效率。要了解这一点,假设并行应用程序使用CAS操作用于线程之间的通信。这些CAS操作通常会涉及缓存未命中,也就是说,假设线程主要是在相互之间进行通信而不是与自身通信。进一步假设每次CAS通信操作对应的工作单元需要300纳秒,这足以计算多个浮点超越函数。那么大约一半的执行时间将被CAS通信操作消耗!这意味着运行此类并行程序的双CPU系统不会比单CPU上运行的顺序实现更快。            

分布式系统的情况更糟,单个通信操作的延迟可能长达数千甚至数百万次浮点运算。这说明了通信操作必须极其不频繁,并且能够支持大量处理的重要性。        

课程应该非常明确:并行算法必须在设计时充分考虑这些硬件特性。一种方法是运行几乎独立的线程。线程之间的通信越少,无论是通过原子操作、锁还是显式消息,应用程序的性能和可扩展性就越好。这种方法将在第五章中简要讨论,在第六章中深入探讨,并在第八章中推向极致。     

另一种方法是确保任何共享数据都是读取优先的,这样可以允许CPU的缓存复制读取优先的数据,进而允许所有CPU快速访问。这种方法在第5.2.4节中有所提及,并在第9章中进行了更深入的探讨。            

简而言之,实现优秀的并行性能和可扩展性意味着努力实现令人尴尬的并行算法和实现,无论是通过仔细选择数据结构和算法,使用现有的并行应用程序和环境,还是将问题转换为令人尴尬的并行形式。    

所以,总结一下: 

1.好消息是多核系统价格低廉,而且很容易获得。   

2.更多的好消息:许多同步操作的过热广告比21世纪初并行系统上的要低得多。     

3.坏消息是缓存mi的开销仍然很高,特别是在大型系统上。      

本书的其余部分描述了处理这个坏消息的方法。

特别是,第4章将介绍一些用于并行编程的低级工具,第5章将研究并行计数的问题和解决方案,第6章将讨论促进性能和可伸缩性的设计学科。         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值