1.1 何去何从的并行计算
我们到底该如何选择呢?本节的目的就是拨云见日。
1.1.1 忘掉那该死的并行
Linus Torvalds是一个传奇式的人物(图1.1),是他给出了Linux的原型,并一直致力于推广和发展Linux系统。1991年,他首先在网络上发布了Linux源码,从此Linux迅速崛起壮大,成为目前使用最广泛的操作系统之一。
图1.1 传奇的Linus Torvalds
自2002年起,Linus就决定使用BitKeeper作为Linux内核开发的版本控制工具,以此来维护Linux的内核源码。BitKeeper是一套分布式版本控制软件,它是一套商用系统,由BitMover公司开发。2005年,BitKeeper宣称发现Linux内核开发人员使用逆向工程来试图解析BitKeeper内部协议。因此,决定向Linus收回BitKeeper授权。虽然Linux核心团队与BitMover公司进行了协商,但是仍无法解决他们之间的分歧。因此,Linus决定自行研发版本控制工具来代替BitKeeper。于是,Git诞生了。
如果你正在使用Git,那么我相信你一定会为Git的魅力所征服;如果你还没有了解过Git,那么我强烈建议你关注一下这款优秀的产品。
而正是这位传奇人物,给目前红红火火的并行计算泼了一大盆冷水。那么,并行计算究竟应该何去何从呢?
在Linus的发言中这么说道:
Where the hell do you envision that those magical parallel algorithms would be used?
The only place where parallelism matters is in graphics or on the server side, where we already largely have it.Pushing it anywhere else is just pointless.
So the whole argument that people should parallelise their code is fundamentally flawed.It rests on incorrect assumptions.It's a fad that has been going on too long.
需要有多么奇葩的想象力才能想象出并行计算的用武之地?
并行计算只能在图像处理和服务端编程两个领域使用,并且它在这两个领域确实有着大量广泛的使用。但是在其他任何地方,并行计算毫无建树!
因此,人们争论是否应该将代码并行化是一个本质上的错误。这完全基于一个错误的假设。“并行”是一个早该结束的时髦用语。
看了这段较为完整的表述,大家应该对Linus的观点有所感触,我对此也表示赞同。与串行程序不同,并行程序的设计和实现异常复杂,不仅体现在程序的功能分离上,多线程间的协调性、乱序性都会成为程序正确执行的障碍。只要你稍不留神,就会失之毫厘,谬以千里!混乱的程序难以阅读、难以理解,更难以调试。所谓并行,也就是把简单问题复杂化的典型。因此,只有“疯子”才会叫嚣并行就是未来(The crazies talking about scaling to hundreds of cores are just that–crazy)。
但是,Linus也提出了两个特例,那就是图像处理和服务端程序是可以也需要使用并行技术的。仔细想想,为什么图像处理和服务端程序是特例呢?
和用户终端程序不同,图像处理往往拥有极大的计算量。一张1024×768像素的图片,包含多达78万6千多个像素。即使将所有的像素遍历一遍,也得花不少时间。更何况,图像处理涉及大量的矩阵计算。矩阵的规模和数量都会非常大。因为如此密集的计算,很有可能超过单核CPU的计算能力,所以自然需要引入多核计算了。
而服务端程序与一般的用户终端程序相比,一方面,服务端程序需要承受很大的用户访问压力。根据淘宝的数据,它在“双11”一天,支付宝核心数据库集群处理了41亿个事务,执行285亿次SQL,生成15TB日志,访问1931亿次内存数据块,发生13亿个物理读。如此密集的访问,恐怕任何一台单核计算机都难以胜任,因此,并行程序也就自然成了唯一的出路。另一方面,服务端程序往往会比用户终端程序拥有更复杂的业务模型。面对复杂业务模型,并行程序会比串行程序更容易适应业务需求,更容易模拟我们的现实世界。毕竟,我们的世界本质上是并行的。比如,当你开开心心去上学的时候,妈妈可能在家里忙着家务,爸爸在外打工赚钱,一家人其乐融融。如果有一天,你需要使用你的计算机来模拟这个场景,你会怎么做呢?如果你就在一个线程里,既做了你自己,又做了妈妈,又做了爸爸,显然这不是一种好的解决方案。但如果你使用三个线程,分别模拟这三个人,一切看起来那么自然,而且容易被人理解。
再举一个专业点的例子,比如基础平台Java虚拟机,虚拟机除了要执行main函数主线程外,还需要做JIT编译,需要做垃圾回收。无论是main函数、JIT编译还是垃圾回收,在虚拟机内部都是一个单独的线程。是什么使得虚拟机的研发人员这么做的呢?显然,这是因为建模的需要。因为这里的每一个任务都是相对独立的。我们不应该将没有关联的业务代码拼凑在一起,分离为不同的线程更容易理解和维护。因此,使用并行也不完全是出于性能的考虑,而有时候,我们会很自然地那么做。
1.1.2 可怕的现实:摩尔定律的失效
摩尔定律是由英特尔创始人之一戈登·摩尔提出来的,其内容为:集成电路上可容纳的电晶体(晶体管)数目,约每隔24个月便会增加一倍。经常被引用的“18个月”,是由英特尔首席执行官大卫·豪斯所说:预计18个月会将芯片的性能提高一倍(即更多的晶体管使其更快)。
说得直白点,就是每18个月到24个月,我们的计算机性能就能翻一番。
反过来说,就是每过18个月到24个月,你在未来用一半的价钱就能买到和现在性能相同的计算设备了。这听起来是一件多么激动人心的事情呀!
但是,摩尔定律并不是一种自然法则或者物理定律,它只是基于人为观测数据对未来的预测。按照这种速度,我们的计算能力将会按照指数速度增长,用不了多久,我们的计算能力就能超越“上帝”了!畅想未来,基于强劲的超级计算机,我们甚至可以模拟整个宇宙。
摩尔定律的有效性已经超过半个世纪了,然而,在2004年,Intel宣布将4GHz芯片的发布时间推迟到2005年,在2004年秋季,Intel宣布彻底取消4GHz计划(如图1.2所示)。
是什么迫使世界顶级的科技巨头放弃4GHz的研发呢?显然,就目前的硅电路而言,很有可能已经走到了头。我们的制造工艺已经精确到了纳米了。1纳米是10-9米,也就是十亿分之一米。这已经是一个相当小的数字了。就目前的科技水平而言,如果无法在物质分子层面以下进行工作,那么也许4GHz的芯片就已经接近理论极限了。因为即使一个水分子,它的直径也有0.4纳米。再往下发展就显得有些困难。当然,如果我们使用完全不同的计算理论或者芯片生成工艺,也许会有本质的突破,但目前还没有看到这种技术被大规模使用的可能。
图1.2 Intel CEO Barret单膝下跪对取消4GHz感到抱歉
因此,摩尔定律在CPU的计算性能上可能已经失效。虽然,现在Intel已经研制出了4GHz芯片,但可以看到,在近10年的发展中,CPU主频的提升已经明显遇到了一些暂时不可逾越的瓶颈。
1.1.3 柳暗花明:不断地前进
虽然CPU的性能已经几近止步,长达半个世纪的摩尔定律轰然倒地,但是这依然没有阻挡科学家和工程师们带领我们不断向前的脚步。
从2005年开始,我们已经不再追求单核的计算速度,而着迷于研究如何将多个独立的计算单元整合到单独的CPU中,也就是我们所说的多核CPU。短短十几年的发展,家用型CPU,比如Intel i7 就可以拥有4核心,甚至8核心。而专业服务器则通常可以配有几个独立的CPU,每一个CPU都拥有多达8个甚至更多的内核。从整体上看,专业服务器的内核总数甚至可以达到几百个。
非常令人激动,摩尔定律在另外一个侧面又生效了。根据这个定律,我们可以预测,每过18个月到24个月,CPU的核心数就会翻一番。用不了多久,拥有几十甚至上百个CPU内核的芯片就能进入千家万户。
顶级计算机科学家唐纳德·尔文·克努斯(Donald Ervin Knuth),如此评价这种情况:在我看来,这种现象(并发)或多或少是由于硬件设计者已经无计可施导致的,他们将摩尔定律失效的责任推给软件开发者。
唐纳德(如图1.3所示)是计算机巨著《计算机程序设计艺术》的作者。《美国科学家》杂志曾将该书与爱因斯坦的《相对论》、狄拉克的《量子力学》和理查·费曼的《量子电动力学》等书并列为20世纪最重要的12本物理科学类专论书之一。
图1.3 唐纳德
1.1.4 光明或是黑暗
根据唐纳德的观点,摩尔定律本应该由硬件开发人员维持。但是,很不幸,硬件工程师似乎已经无计可施了。为了继续保持性能的高速发展,硬件工程师破天荒地想出了将多个CPU内核塞进一个CPU里的奇妙想法。由此,并行计算就被非常自然地推广开来,随之而来的问题也层出不穷,程序员的黑暗时期也随之到来。简化的硬件设计方案必然带来软件设计的复杂性。换句话说,软件工程师正在为硬件工程师无法完成的工作负责,因此,也就有了唐纳德的“他们将摩尔定律失效的责任推给了软件开发者”的说法。
所以,如何让多个CPU有效并且正确地工作也就成了一门技术,甚至是很大的学问。比如,多线程间如何保证线程安全,如何正确理解线程间的无序性、可见性,如何尽可能地设计并行程序,如何将串行程序改造为并行程序。而对并行计算的研究,也就是希望给这片黑暗带来光明。