免费午餐已结束:并发成为软件的基本转向

2016.01.13 - 01.22
个人英文阅读练习笔记。原文地址:http://www.gotw.ca/publications/concurrency-ddj.htm

01.13
免费午餐已结束
并发成为软件的基本转向
由Herb Sutter
由于/自从面向对象的演变,软件开发最大改变正敲门而来,它的名字叫并发。
该文出现在Dr.Dobb’s Joural, 30(3),2005 3月。一个更加简单的版本在C/C++ Users Journal, 23(2),2005 2月的”并发演变”标题下。

更新笔记:2009年8月最近更新的CPU趋势图包含了当初预测的当前数据并展示了持续趋势。本文后续内容保留/复制了2004年12月最初的所有文字。

01.14
免费午餐即将结束。对此您能够做什么?对此您正在做什么?
处理器主要的制造和架构商,从Intel和AMD到Sparc和PowerPC,已经突破大多数传统的提升CPU性能的方法。替代之前加快驱动时钟和加大流水线指令吞吐量,他们现在转向超线程和多核的架构体系中。这两个功能已在现在的某些芯片上存在;特别地,多核功能已在如今的PowerPC和Sparc IV处理器中,且在2005Intel和ADM也将实现多核处理器。实际上,2004在In-Stat/MDR秋季处理器会(Fall Processor Forum)的大主题就是多核设备,当时许多公司展示了新的或更新的多核处理器。回顾此会,将2004年称为多核年一点也不夸张。

这一转变给软件开发领域带来一个基本的转折点,至少在接下来几年里以通用桌面计算机和低端服务器(如今占绝大多数市场的那些软件所运行的机子)为目标的软件开发来说。在本文中,我将描述硬件域的改变,为什么它突然就对软件开发有了关系,并描述具体的并发改革是如何影响了您以及它将改变的方式(这将可能成为您将来编写软件的方式)。

可以说,免费的午餐已经结束一年或两年了,只是我们现在才开始注意这一点。

CPU免费的性能午餐

有一个有名的有趣现象 - “安迪所赐,比尔夺去(Andy giveth, and Bill taketh away)”。无论处理器能获得多快的速度,软件总能够耗尽处理器额外的速度。制造一个10倍快的CPU,软件将能找到10倍多的事情来做(或者说在某些情况下,将事情低效的分为10次来做会感觉到更自由/轻松)。几十年来,应用程序的大多数类已经喜欢上免费和规律的性能增益,即使软件类没有发布新版本或做任何特殊的改动,因为CPU制造商(主要的)和内存以及硬盘制造商已经持续的更新和更快的主流系统。失踪速度不是性能的唯一测量,甚至不是好的/准确一个标准,但它是代表性、指示性的一个。我们曾看到500MHz到1GHz再到2GHz等过程。现在我们的CPU在主流水线上的时钟约在3GHz范围。

主要/关键的问题是:时钟频率增长会在何时结束?毕竟,摩尔定律预测的指数增长清楚的表明了该指数增长不会永远的持续下去 - 会遇到物理硬件的限制;光速不会变得更快。此增长速度最终定会减慢甚至停止。(说明:摩尔定律主要应用于晶体管的集成,但与该定律相同的指数增长也发生在了诸如时钟速度的相关领域中。在某些领域甚至增加得更快,尤其是数据存储爆炸,但这个重要趋势不属于本文讨论范畴。)

如果您是一个软件开发者,改变是您已经乘坐在桌面电脑性能的“免费午餐”的风口浪尖。您的应用程序在一些局部操作中是否处于边界之上?“不用担心”,传统(若怀疑)的智慧告诉我们,“明日的处理器将拥有更高的吞吐量,现在的应用程序是因为其本身的数量而被压制,而不是因为CPU的吞吐量和内存速度(如常见的I/O限制,网络限制,数据库限制)。”对吗?

在过去,此观点完全对。但在可预测的将来却是绝对错误的。
好消息是处理器将继续变得更加的强大。坏消息是,至少在短期,这种增长的大体方向不会让应用程序延续之前的免费使用习惯。

在过去的30年里,CPU设计者主要在3个主要领域获取性能增益,前两个集中于流水线执行:

  • 时钟速度
  • 执行优化
  • 缓存
    增加时钟速度是为获取更多的周期。运行更快的CPU多多少少地直接意味着做相同工作会更快。

优化执行流是为在每个周期中做更多的工作。现在的CPU能运行更强大的指令,它们执行优化的范围从一般到奇异,包括管道、分支预测、正在同一个时钟周期内执行多条指令甚至更快以及通过缩减延迟和最大化每个时钟周期的工作量来在每个时钟周期中序列化大多数工作。

01.15
芯片设计者在太大的压力下冒险改变并有可能完全打破程序的含义来让程序运行得更快以制造出更快的CPU
简单地提一下指令重序和内存模式:注意本文中所提到的有些“优化”实际上远超优化,它们指的是可以改变程序含义并引起能够打破程序员合理的预期的可见效果。这是有重大意义的。通常来看,CPU设计者通常是理智且能与民同乐不会伤害一只苍蝇也不会想着伤害您代码的人。但最近几年他们已经甘于追逐具有侵略性的优化只为拧出更快的时钟周期,尽管他们完全清楚这些侵略性的重新安排可能会代码的语义带来危险。这就是Mr.Hyde的露面?并不是。这种意愿是芯片设计者面对延续更快CPU的一种极端压力的简单地清晰的暗示;他们承受太大的压力来冒险改变并有可能完全程序的含义来让程序运行得更快。在这方面有两个值得一提的是写重序和读重序:允许处理器重序写操作已经导致了惊讶并打破了许多程序员的预期,该特性已经被取消,因为让程序员以任意写重序的方式正确的理解程序的含义太难。重序读操作同样产生了显见的惊讶效果,不过这个功能可以考虑被保留下来,因为这对程序员来说不那么难,对该性能的需求会引起操作系统和操作环境的设计者来折中和选择将更大的压力留给程序员,因为这样做似乎比直接放弃该优化机会少些罪恶感。

01.16
最后一点,是增加芯片上的缓存的容量来远离直接访问RAM。主存持续比CPU慢许多的特点让将数据放置到离CPU近端的方式变得敏感 —- 并且不能近于高速缓存。高速缓存的容量一被猛增,且现在的大多数的主要芯片供应商都是卖的板上拥有2MB或更大L2缓存的CPU。(在这3种用来增加CPU性能的方法中,只有增加缓存在近期还会持续使用。本文将在后面在稍微多介绍一点关于缓存的重要性。)

那么这将意味着什么?

在这几个方法中至关重要的一点是它们都与并发无关。在这几个中的任何一领域中加速将直接导致顺序化(非平行,单线程,单进程)应用程序运行的加速,除了实实在在地使用了并发的应用程序。这很重要,因为现在绝大数的应用程序都是单线程的,将在下文解释该原因。

当然,编译器已经跟上了步伐;有时需要重新编译应用程序并指定一个最低级版本的CPU目标,以从新的指令(如MMX, SSE)和一些新CPU的功能和特性中获益。但是,总的来说,甚至有的旧的应用程序也会运行得更快 —- 即使没有被重新编译来使用最新CPU提供的新的指令和新的功能特性。

旧程序依旧可以运行得更快是一个令程序员欢欣鼓舞的一点。但不幸的是,它已经消失了。

01.17

障碍,为何今天没有10GHz频率的CPU

CPU性能增长在两年前已经遇到了阻碍墙。大多数人只是在最近才开始注意到。

使用Intel的数据来说明这个问题,根据其它的芯片也能获取到类似的图表。图1介绍了Intel芯片的时钟和晶体管数量的历史数据。至少在目前为止,晶体管的数量还在持续上爬。但时钟已开始另一段历史。
这里写图片描述
图1. Intel CPU介绍(图更新于2009年8月;文章内容为2004年12月)

约在2003年开始,时钟速度在之前促成更快速CPU的趋势中突然转折。图中用增加的线条来展示时钟速度之前最大增加的趋势/规律;但时钟并没有按照之前的规律持续增长,而是按照小点路线在变化,时钟增长的趋势似已达瓶颈(扁平化增长的趋势)。由于不仅是单个问题而是多个物理问题的存在,开发出更快的时钟频率变得越来越难,尤其是热量(大多的热量且难以散发这些热量),电量消耗(太高)以及电流泄露问题。

在您的电脑上的CPU上时钟频率是多少?有10GHz么?在Intel 芯片中,在较长时间前(2001年8月)就获得了2GHz的时钟频率,根据2003年之前的CPU发展趋势,在2005早期我们应该首次拥有时钟为10GHz的奔腾系列芯片。很明显的是,我们周围都没有10GHz时钟频率的CPU出现。另外,这样的芯片还并未出现在咱们的视线范围内 —- 我们不知道何时能够看到这样的CPU芯片的诞生。

既然如此,那么关于4GHz时钟频率的CPU呢?我们已经在使用3.4GHz的了 – 这表示着4GHz已经不远了?实际上,可惜即使4GHz也很遥远。您可能知道在2004年中期,Intel首次否认在2005之前不会计划4GHz的芯片,并在2004的秋季官方宣布完全放弃4GHz的计划。在写本文时,Intel计划在2005的早期(已包含在图1中的最右上角的点中)将时钟频率慢慢地稍微提升到3.73GHz,但时钟的竞赛已经结束了,至少对于现在来说;Intel和大多数处理器供应商将来将会把策略放在多核方向上。

我们可能会在某天在主流的桌面机器中看到4GHz的CPU,但绝不会是在2005。当然,Intel在他们的实验室中有可以运行的更高时钟频率的CPU – 但这仅是精英结合的结果,如附加大量可怕的不可能在实际中使用的经冷却的材料。在不远的将来里,在您的办公室中的正在进行各种计算的平面电脑上不会有这种类型的冷却硬件。

无免费午餐(TANSTAAFL):摩尔定律和下一代(处理器)

01.18
“天下没有免费的午餐。”—- R.A.Heinlein,月亮是个严厉的女主人(The Moon Is Harsh Mistress)。

这是否意味着摩尔定律不再使用了?有趣的是,从正常来看,答案却并不是。当然,就像所有的指数级数一样,摩尔定律会在某天结束,但这并不意味着在接下来的几年里摩尔定律就会有这种危险。尽管芯片设计工程师在时钟周期这个点上已经遇到了阻碍墙,但晶体管的数量还在持续的以爆炸式的方式增长并且如CPU的吞吐量增益在接下来的几年中也会以摩尔定律的规律持续增长下去。

关键区别即本文的中心,至少对于接下来的基带处理器芯片来说,它们的性能增益会用根本不同的方式来完成。并且对于现在大多数的应用程序来说,如果不对其进行重新设计,那么它们并不会再收益于新CPU的性能。

在近期即在接下来的几年里,新一代的CPU的性能增益主要来自3个主要的方法,其中只有一个方法与过去相同。近期会驱动性能的3个方法是:

  • 超线程
  • 多核
  • 高速缓存

01.19
超线程是关于两个或多个线程在单个CPU内并行运行的描述。如今已有支持超线程的CPU,它们的确能够让有些指令并行的运行。然而,有一个限制因素即尽管超线程的CPU有一些包括额外寄存器的额外硬件,它仍旧只有一个缓存、整数数学运算单元、FPU且它们都是一些CPU中最基础的原件。对于用编写的合理的多线程应用程序,超线程有时能够将性能提升5%到15%,甚至提升到40%(对于仔细编写的理论下的多线程程序)。这是一个好现象,但是它几乎达不到双,并且这样的CPU也不会帮助单线程应用程序提升性能(运行速度)。

多核是关于在芯片上同时运行两个或多个实际的CPU的现象。已有一些芯片,包括Sparc和PowerPC,已有可用的多核版本。Intel和AMD最初的设计,都是在2005,对集成层次进行了变动但功能都相似。AMD的CPU拥有一些最初性能设计优势,如对支持相同高速缓存中的函数的更好集成,然而Intel最初的模块只是基础的在单缓存上集成两个Xeons。性能增益最初应该和用用真实的双核CPU系统(只是该系统会更便宜,因为主板并不必含有两个插口和集成两个核)相同,这就意味着即使处于理想状况下也达不到双核的速度,就像现在会合理提升编写良好的多线程应用程序的性能而不会提升单线程应用程序性能一样。

至少在目前看来,在线高速缓存的容量还能持续增加以提升性能。在这3个领域中,只有高速缓存能够让已经存在的应用程序的性能收益。在线高速缓存容量的持续增长对许多应用程序来说都是及其重要且有高度的适用性,因为空间就是速度。直接访问主存会耗时得多,如果能的话都会尽量避免直接访问RAM。在如今的系统上,若缓存没命中则会重新读主存,跟直接从缓存读取数据相比,这常会消耗10到50倍的时间;顺便提一下,这将持续惊讶到我们,因为我们都认为主存已经很快了,它跟磁盘和网络比较它的确足够快,但跟以更高速度运行在板上的缓存还没可比性。如果所编写的应用程序能够高度配合缓存命中,我们已经胜利了,但如果不能够配合,我们就还没有彻底胜利。这就是为什么增加缓存的容量会保留一些应用程序并能将它们的生命再延续几年(在不对这些程序进行重新设计的情况下):因为已存在的应用程序会操作更多的数据,当更新这些程序包含更多代码来适应新特性时,性能敏感的操作需要持续保持配合缓存机制。令人犹豫过的往日时间将会提醒您 - 缓存就是王道。

(注/旁白:最近在我编译器团队发生了一个可证明“空间就是速度”的轶事。基于32位和64位的编译器使用相同的源代码;这些代码仅在32位或64位的处理器上编译通过。64位编译器运行在64位CPU上时获得了基线性能的很大交易,主要是因为64位CPU有更多可使用的寄存器并且有其它的代码性能特性。这固然是好。但是关于数据呢?达到64位不会改变内存中的数据的大小,除了指针所占内存的大小会变成32位系统上指针的两倍以外。当运行时,编译器会使用在内部数据结构中的比之前其它应用程序占用更大内存的指针。因为指针变成了8个字节而不是之前的4字节,在64位编译器工作下,应用程序会有一个纯粹的数据耗存的增加。这个更大值的设置会使性能受到处罚,从拥有更多寄存器的更快处理器中去让大多数可执行代码的偏移性能增加。当在写本文时,64位的编译器跟32位编译器的运行速度一样,即使源库都相同并且64位处理器提供更好的处理吞吐量。空间就是速度。)

正是缓存的存在。超线程和多核CPU近乎不影响现在存在的大多数应用程序。

所以,硬件中的这种改变给我们编写软件的方式带来何种意义?到目前为止您可能已经注意到了这个答案,所以让我们共同来思考它和其后果吧。

01.21

神话与现实:2 x 3GHz < 6GHz

一个拥有两个3GHz核的双核CPU实际能够提供6GHz的处理能力。对么?

错误。即使两个线程运行在两个实际的处理器上也并不意味着能够获取两倍的性能。同理,大多数多线程应用程序在双核上运行并不会快两倍。它们应该比运行在单核上快一些;性能增益并不是呈线性增长,仅此而已。

为什么不呈线性增长?首先,核与核之间有协调开销以确保高速缓存的一致性(将缓存和内存看作连续的)且还要执行其它的信息交换操作。现在的双核或四核处理器并不是单核CPU的两倍或四倍,即使运行多线程应用程序。跟CPU在相同的在线高速缓存上的问题基本相同。

其次,除非各核分别运行不同的进程或单线程中不同的线程 -被编写的十分优秀能够独立的运行且几乎不会等待彼此,它们彼此间不会相互利用。(尽管如此,我猜测现在的单线程应用程序被用于双核上能实实在在看到性能提升的场景,并不是因为另外一个核做了什么实在有用的事情,而是因为附带运行的广告和间谍软/监控件插入在了许多用户系统中并占用了用户使用的单核CPU。然后留给您做决定是否要增加一个CPU来运行间谍软件)

若果运行单线程应用程序,应用程序智能使用一个核。这会带来增速效果,因为操作系统和应用程序可以独立的运行在一个核之上,但典型的操作系统不会完全将其它CPU的权限交给用户,所以其它的核是几乎空闲的状态。(再次提一下,间谍程序可以在大部分时间里共享操作系统的核。)

01.20

这对软件来说意味着什么:下一代演变

在20世纪90年代,我们学习并体会了对象。在过去20甚至可以说是30年里,从面向结构编程到面向对象编程是主流软件开发最大的演变。在这些年里也曾有过其它的改变,包括最近(且是真正有趣的)诞生的万维网服务,但在大多数人的工作中并没有看到跟面相对象演变那样的本质和深远的编写软件方式的改变。

直到现在为止,真正的一场演变才又到来。
从现在起,不再有免费的性能午餐。当然,由于高速缓存容量的提升,应用程序可以持续获取普遍适用的性能增益。但如果欲让应用程序受益于新处理器上持续指数规律增长的吞吐量优势,它就需要以并发(通常为多线程)的方式很好的编写应用程序。说起来比做起来可能比较难,因为不是所有的问题都固有并发的属性且并发编程本身也很难。

我可以听到反对的意见:“并发?并不是新闻。人们已经在编写并发应用程序了。”这也是事实。但这只代表一小部分开发者。

注意,至少从20世纪60年代起就已经有人在用模拟语言开始做面向对象编程了。但面向对象并没有成为编程界的重大演变,直到20世纪90年代才主导主流软件开发。这是为何?面向对象编程的演变的主要原因是因为工业有编写解决越来越大的问题的越来越大的应用程序的需要、指数增长规律产生了越来越快的CPU以及存储资源变得可用。面向对象编程在抽象和依赖管理方面的优势让其能够实现大范围的软件开发且经济、可靠、可重用。

并发是我们如何编写软件的下一代演变
同样,自从演变这个忧郁的年代开始后我们就开始做并发编程,编写协同程序、监视程序以及类似的所谓爵士的东西。在过去十年我们见证了越来越多程序员开始编写并发(多线程,多进程)系统。但由主要转折点标志的实际的并发演变已经慢慢的实现了。如今绝大多数的应用程序还是单线程,我们在下文解释这个现象的原因。

顺便提一下炒作的问题:人们总是很快去宣传“下一代软件开发改革”,常是他们自己全新的技术。不要相信。新技术常都是真正有趣并有时是有益的,但编写软件中最大的演变技术在它大范围扩展之前通常是已经存在了几年且已经有了一个逐渐扩展的过程。这是必要的:可以只基于某个足够成熟的软件开发技术(包括有实在的供应商和工具支持)来构建软件,这样通常能够使用使用任何新的软件开发技术(至少在该技术已经足够成熟不再有陷阱被开始广泛广泛使用前)。因此,像面向对象这种真实的软件开发技术已经被提炼的许多年,常在10年左右。即使在好莱坞,大多数有趣的“一夜成名”,在他们取得这个重大突破前都已经准备了多年。

并发是我们如何编写软件的下一代演变/改革。不同的专家对并发是否会超越面向对象持有不同的观点,这种类型的会谈也最好是留给这些专家们。有趣的是,对于技术专家来说,在软件开发中(预期)的改革/演变、复杂程度和技术学习路线来说,并发和面向对象具有相同的排名。

01.21

并发编程的好处和代价

使用并发编程有两个主要的原因,尤其是多线程,它已经被用到了主流软件中。并发编程的第一个原因它拥有逻辑上独立的控制流;例如我设计的一个数据库响应服务器,它将每个响应会话都放在单独的线程中,因为每个会话完全独立于其它的可能也处于活跃状态的会话(只要它们没有工作在同一个数据库行上)。并发编程的第二个不常见的原因是在过去写并发程序是为了提升性能,以分级利用复杂CPUs的物理特性或简单的利用其它应用程序运行时的延迟;在我自己的数据库响应服务器程序中包含此点,并且当服务器跟许多其它的服务器连接并处理越来越多的并发响应会话时本程序也能够分级应用CPU的特性。

然而,并发编程也会有代价。一些明显的代价实际并不那么重要。如比较难以获取到锁,但与放弃同步而言,只要能够找到一个关键的方法来平行这些操作并最小化甚至消除共享状态,只要明智恰当的使用锁就能从并发执行中获取更多的好处。

并发编程其次最大的代价恐是并不是所有的应用程序都适合并行化。我将在稍后为此介绍更多。

并发编程最大的代价可能要属于并发编程本身就很难:编程模式,这就意味着在程序员脑中的编程模型要合理可靠的用到实际编程中,这比以前的顺序控制流要困难得多。

每个学习并发的人认为他们了解它,最终以找到他们认为不可能的谜种并发现他们实际上并不理解并发编程结束。当开发者学习思考并发时,他们可以通过在房间里面进行合理的测试能够找到一些谜种,然后他们将到达一个高度并同时觉之欣慰。然而通常不能从测试中获取到的呢,除非是在商店里面理解为何以及如何做实际的街道测试,一些潜在的并发臭虫只会在多核处理器系统上才暴漏出来,在多核处理器上,线程不会被切换到单核环境中并且各线程以真实的同时运行的状态执行,这样就会暴漏出新的错误。这是那些确信他们知道如何编写并发程序的人们的下一个挫折:我见过许多团队的应用程序工作良好甚至经过严密和扩展的强度测试,并且在许多自定义的网站上也能完美运行,直到将该程序运行在一台真正的多处理机器上时,深度的谜种和变体开始间歇地被列在清单文件中。在如今的CPU蓝图中,重新设计应用程序将多线程运行在多核机器上有点像跳入深水中学习游泳,真实的并行环境是最有可能暴漏在程序中所犯的错误。即使是一个善于写稳定可靠并行程序的团队,也有其它的陷阱;如并发程序完全安全但没有比它在单核机器上运行得更快,典型的原因是因为线程没有完全独立开并在单一资源上共享依赖使得重新序列化了程序的执行。这个东西(编发)变得异常微妙。

就像一个结构化编程的程序员学习面向对象这个跳跃一样(什么是对象?什么是虚拟函数?应该如何使用继承?抛开“是什么”和“怎么做”,为什么正确的实践步骤在实际中就是正确的呢?)对于序列化的程序员学习并发编程来说同样是一个巨大的跳跃(什么是竞争?什么是死锁?它何时发生并如何避免它的发生?什么样的构建(在脑海中思考的是并行)实际上会序列化程序?信息队列是怎么样的?抛开“是什么”和“怎么做”,为什么正确的设计在实际中就能够正确地运行?

目前的绝大多数程序员都没有准确理解并发,就像15年前绝大多数程序员并未准确理解对象一样
目前的绝大多数程序员都没有准确理解并发,就像15年前绝大多数程序员并未准确理解对象一样。但是并发编程模型是可学习的,尤其是如果坚持使用信息和基于上锁(message-and lock-base)的编程方式,一旦理解它并不比对象难许多并充满希望就可能将其变成一种自然的理解。只是要准备并允许在训练和时间上有所投入,为您自己还有您的团队。

(我故意限制了以上提到的信息和基于上锁的并发编程模型。还有像无锁这样的编程方式,java 5和至少一种流行的C++编译器直接支持这种编程方式。但是并发无锁编程非常难被程序员所理解和思考,甚至比基于锁的编程方式还要难。多数情况下,只有编写系统和库的程序员不得不理解无锁编程模式,尽管实际上人人都应该能够利用这些人构造的无锁系统和库。坦白讲,即使是基于锁的编程方式也是冒险的。)

并发对我们意味着什么

回归到并发对我们意味着什么的话题上。

1.一个明显的结果是已经提到过的,若欲想充分地利用已可用的和在接下来几年里将要持续实现的CPU的吞吐量增益应该增加并发的应用程序。如Intel正在谈论在某天产生100核的芯片;单线程应用程序最多可以利用这样芯片的1/100的潜在吞吐量。“哎,应用程序本身的性能并没那么重要,反正计算机会持续变得更快”这种天真的话语应当被质疑,并在不久的将来该观点就会成为比较显然的错误。

如今,并不是所有的应用程序(或更精确的描述,是应用程序中重要的操作)都能被并行化。确实,诸如编译这类问题,大部分都可以并行化。但其它的则不能;一个常见的反例是一个母亲可以花九个月生一个孩子但九个女人并不能只花一个月生一个孩子。您可能之前遇到过类似的情况。但您曾在扔下这个类似问题时留意这个问题了么?有一个带陷阱的问题可以用来反问问你这个问题的人:您能够从人类孩子问题上推测出固有的不能被并行化么?通常人们会快速的推测出该问题证明了固有不能并行的问题的错误推理,但这并不是完全正确的。如果目标是生一个小孩的话这确实是一个固有非并行化的问题。如果目标是生很多小孩的话它其实是一个理想的可并行化的问题。当思考软件是否可并行化以及并行化软件的时候应该时刻在脑海中保持“了解真实的目标会大不相同”这种面向目标的基本准则。

01.22
2.一个不那么明显的结果是应用程序可能越来越多的变为CPU密集型(CPU-bound)。当然,并不是每个应用程序操作都会是CPU-bound,如果没有准备就绪这些操作就会受到影响而不会很快变为CPU密集型,但似乎已经到达了“越来越多的应用程序成为I/O密集型(I/O-bound),网络密集型(network-bound)或数据库密集型(database-bound)”趋势的尽头了,因为在这些领域的性能还在快速的上升(有谁在使用千兆WiFi么?),然而传统的CPU性能-增益技术已经达到最高极限了。思考:我们现在停留在3GHz左右的频率上。因此单线程应用程序可能不会再获得更快的运行速度除非从容量增加的缓存上获得好处(这是一个主要的好新闻)。其它的收益可能会增加但会比之前常常看到的增加的速度要慢,如当芯片设计者找到新的方式来保持满管道和避免拖延时,低挂领域的果实已经丰收了。对新应用程序的需求不太可能会减少甚至会对应用程序要求更多,以处理应用程序中增加的数据(这种需求不会减速)。由于持续的要求应用程序做得更多,他们也发现了已耗尽了CPU来做这些事情,由此需要编写并发的代码。

有两个方法来应对并发这种海变。一是重新设计应用程序成为以上提到的并发程序。另一个方法是节约 - 通过编写更高效且更少浪费的代码。这将导致第三个有趣的结果。

3.效率和性能的优化将会变得更加重要。一些语言需要将它们置于重大的优化中以寻找到新的生机;一些则不需要去寻找方法来让自己变得更高效。预计会长期需要面向性能的语言和系统。

4.越来越多的编程语言和系统将会被强制去处理并发。Java在一开始就已经包含了对并发的支持,尽管其中包含的错误用了好几个发行的版本来修复,为能更高效和正确的做到并发。C++被用来编写重任务的多线程系统也有好长时间了,但是它并没有支持并发的标准(ISO C++甚至都有意不提线程),所以典型的C++并发中的必要程序部分都是通过一些不可移植的平台指定的并发特性和库来完成的。(它常是不完全的,如静态变量只能被初始化一次,这典型需要编译器用锁的形式包装,但许多C++的实现并会产生锁。)总算有几个并发的标准,包括pthread和OpenMP,以及一些隐式支持这些标准的并显示并行化的库。在编译器在编译单线程程序时它会自动的计算出如何将该程序隐式的并发化才最好,但这些自动转换工具毕竟功能有限也不如本身的并发代码。关于基于锁的编程模式艺术思想的主流状态是微妙和冒险的。我们急切地需要一种高于现在这些语言所提供的并发编程水平的编程模式;我会继续对此讨论更多。

结论

如果您现在还未这样做,现在是时候花点功夫来看一下您应用程序的设计,判断一下哪些操作是现在或不久的将来里是CPU敏感型的,并确认这些操作如何才能从并发获取好处。现在也是您以及您的团队去理解并发编程的需要、陷阱、模式以及风格的时候。

应用程序中的一些罕见的类是并行的,但多数都不是这样的。即使当你准确的知道哪里为CPU-bound,您可能容易发现推算出如何并行这些操作比较难;所有最有理由现在开始思考它。编译器的隐式并行能够帮助一小点,但是别对此抱太大期望;它所并发的应用程序中的序列化代码不如您将其显示地转换为并行代码和线程的版本。

由于高速缓存容量的持续增加以及可能的更多流水线控制流的优化,免费的午餐可能还会持续一小段时间;但从现在起该自助餐将只提供一道主菜且这道主菜只是个甜点。吞吐量这道小牛排依旧还在菜单中,但现在点它会耗更多代价 — 更多的开发功夫、更多的代码复杂度以及更多的测试。好消息是,对于应用程序的许多类来说这些努力将会是有价值的,因为并发会让它们完全利用持续以指数增长的处理器吞吐量收益。
[2016.01.13 - 10:23]

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值