1:为什么需要并行
- Linus是一个传奇的人物,是他给出了Linux的原型,并且一直致力于推广和发展Linux系统。1991年,他在网络上发布Linux源码,从此Linux迅速崛起壮大,成为目前最广泛的操作系统之一。由于他和BitMover之间的分歧,因此Linus决定自行研发版本控制工具,于是,Git诞生了。正是这位传奇人物,给目前红红火火的并行计算泼了一个凉水,他说到:并行计算只可以在图像处理和服务端编程两个领域使用,但是在其他地方,并启计算毫无建树,因此人们争论是否将代码并行化是一个本质上错误。这完全是一个错误的假设。“并行”是一个早该结束的时髦用语。因为与串行程序不同,并行程序的设计和实现异常复杂,不仅体现在程序的功能分离上,多线程的协调性,乱序性多会成为程序正确执行的障碍。所谓并行,也就是把简单问题复杂化的典型。因此只有疯子才会叫嚣并行就是未来。
- 但是 Linus也提出两个特例,那就是图像处理和服务端编程是可以需要使用并行技术的。为什么呢?
和用户终端程序不同,图像处理往往拥有极大的计算量。一张1024X 768像素的图片,包含多达78万6干多个像素。即使将所有的像素遍历一遍, 也得花不少时间。更何况,图像处理涉及大量的矩阵计算。矩阵的规模和数量都会非常大。因为如此密集的计算,很有可能超过单核CPU的计算能力,所以自然需要引入多核计算了。
而服务端程序与一般的用户终端程序相比,一方面, 服务端程序需要承受很大的用户访问压力。根据淘宝的数据,它在“双11”一天,支付宝核心数据库集群处理了41亿个事务,执行285亿次SQL, 生成15TB日志,访问1931亿次内存数据块,发生13亿个物理读。如此密集的访问,恐怕任何一台 单核计算机都难以胜任,因此,并行程序也就自然成了唯一的出路。另一方面, 服务端程序往往会比用户终端程序拥有更复杂的业务模型。面对复杂业务模型,并行程序会比串行程序更容易适应业务需求,更容易模拟我们的现实世界。 毕竟,我们的世界本质上是并行的。比如,当你开开心心去上学的时候,妈妈可能在家里忙着家务,爸爸在外打工赚钱,一家人其乐融融。 如果有一天, 你需要使用你的计算机来模拟这个场景,你会怎么做呢?如果你就在一个线程里,既做了你自己,又做了妈妈,又做了爸爸,显然这不是一种好的解决方案。但如果你使用三个线程,分别模拟这三个人,一切看起来那么自然,而且容易被理解。
- 摩尔定律是由英特尔创始人之.戈登 .摩尔提出说得直白点,就是每18个月到24个月,我们的计算机性能就能翻一 番。反过来说,就是每过18个月到24个月,你在未来使用一半的价钱就能买到和现在的性能相同的计算设备。但是由于目前的硅电路而言,我们制造工艺已经到达纳米了。就目前科研水平,如果无法在物质分子下面工作,那么也许4GHZ的芯片就已经接近理论极限了。可以看出,在近十年的发展中,CPU主频的提升已经明显遇到了一些不可以逾越的瓶颈
- 虽然CPU的性能已经几近止步,长达半个世纪的摩尔定律轰然倒地,但是这依然没有阻挡科学家和工程师们带领我们不断向前的脚步。从2005年开始,我们已经不再追求单核的计算速度,而着迷于研究如何将多个独立的计算单元整合到单独的CPU中,也就是我们所说的多核CPU。短短十几年的发展,家用型CPU,比如Inteli7就可以拥有4核心,甚至8核心。而专业服务器则通常可以配有几个独立的CPU,每一个CPU都拥有多达8个甚至更多的内核。从整体上看,专业服务器的内核总数甚至可以达到几百个。非常令人激动,摩尔定律在另外一个侧面又生效了。根据这个定律,我们可以预测,每过18个月到24个月,CPU的核心数就会翻一番。 用不了多久,拥有几十甚至上百个CPU内核的芯片就能进入千家万户。顶级计算机科学家唐纳德.尔文.克努斯(Donald Ervin Knuth),如此评价这种情况:在我看来,这种现象(并发)或多或少是由于硬件设计者已经无计可施导致的,他们将摩尔定律失效的责任推给了软件开发者
- 根据唐纳德的观点,摩尔定律本应该由硬件开发人员维持。但是,很不幸,硬件工程师已经无计可施。为了继续保持性能的高速发展,硬件工程师破天荒地想出将许多个CPU内核塞进一个CPU里的奇妙想法。由此,并行计算就被非常自然地推广开来,随之的问题也层出不穷,程序员的黑暗时期也随之道来。简化的硬件方案必然带来软件设计的复杂性。换句话说,软件工程师正在为硬件工程师无法完成的工作负责,因此,也就有 了唐纳德的“他们将摩尔定律失效的责任推给了软件开发者”的说法。所以如何让多个CPU有效并且正确地工作也就成了一门技术,甚至是很大的学问,比如多线程间如何可保证线程安全,如何正确理解线程间的无序性、可见性,如何尽可能地设计并行程序,如何将串行程序改造为并行程序。而对并行计算的研究,也就是希望给这片黑暗带来光明
2:有关并行必须知道的几个概念
现在,并行计算显然已经成为一门正式的学科。也许很多人(包括Linus在内)都觉得并行计算或者说并行算法是多么的奇葩。但是我们也不得不承认并行还是有用武之地的。既然说服务端编程还是需要大量并行计算的,而Java也主要占领着服务端市场,那么对Java并行计算的研究也就显得非常必要。但我想在这里先介绍几个重要的相关概念。
Java高并发第一讲 经典易混淆的基础概念
3:并发级别
由于临界区的存在,多线程之间的并发必须受到控制。根据控制并发的策略,我们可以把并发的级别分为阻塞,无饥饿,无障碍,无锁,无等待几种。
Java高并发系列第二讲 并发级别
4:有关并行的两个重要定律
有关为什么要使用并行程序的问题前面已经进行了简单的探讨。总的来说,最重要应该是出于两个目的。第一,为了获得更好的性能;第二,由于业务模型的需要,确实要多个执行实体。在这里,我将更加关注于第一种情况, 也就是有关性能的问题。将串行程序改造为并发程序,一般来说可以提高程序的整体性能,但是究竟能提高多少,甚至究竟是否真的可以提高,还是一个需要研究的问题。目前, 主要有两个定律对这个问题行解答,一个是Amdahl定律,另外一个是Gustafson定律。
有关并行的两个重要定律
5: 回到Java:JMM
由于并发程序要比串行程序复杂的多,其中一个重要的原因是并发程序中数据访问的一致性和安全性会受到严重的挑战。如何保证一个线程可以看到正确的数据呢?这个问题看起来很白痴。对于串行程序来说,就是小菜一碟,如果你读取一个变量,这个变量值是1,那么你读到的一定是1,就是这么简单的问题在并行程序中居然变得复杂起来。事实上,如果不加控制地任由线程胡乱并行,即使原本是1的数值,我们也有可能读到2。因此我们需要在深入了解并行机制的前提下,在定义一种规则,保证多个线程之间可以有效的正确地协同工作。而JMM也就是为此而生。JMM的关键技术点都是围绕着多线程的原子性,可见性和有序性建立的。因此我们必须先了解这些概念。
Java高并发系列第四讲 JMM相关的一些概念