文/王越
在美国宾夕法尼亚州的东部,有一个风景秀美的城市叫费城。在这个城市诞生了一系列改变世界的奇迹:第一个三权分立的国家——美立坚合众国,就在第五街的路口诞生;举世闻名的费城交响乐团,1900年在市中心的 Academy of Music 奏响了他们的第一个音符。而写这篇文章时,我正坐在三十四街的宾夕法尼亚大学计算机系的一楼实验室,面前摆放着世界上第一台电子计算机——ENIAC。
1946年 2 月 14 日,ENIAC 问世,每秒可运行 5000 次加法运算或 500 次乘法运算,面积达 170 平方米,重约 30 吨,拉开了计算机处理器革命的序幕。这场革命是各处理器厂商长达数十年的竞赛,而摩尔定律从一开始就准确地预测了这场比赛的走势。根据摩尔定律,同样价格的集成电路上可容纳的晶体管数目,每隔约 18 个月便会增加一倍,性能也将提升一倍。但事实上,并无法用老路子来保持这个增长速度,因为会遇到包括能耗、散热等各种技术瓶颈。所以每隔几年就会有用来绕过这些瓶颈的新一代产品推出。如采用超纯量(superscala)、指令管线化、快取等。这些技术通过一定程度的高效并行来挖掘计算机处理器的速度所能达到的高度,以促使用户更新换代。
世界上第一台计算机 ENIAC,1946年 2 月 14 日诞生于宾夕法尼亚大学
和 66 年前的 ENIAC 相比,今天的处理器已有了质的飞越。而 21 世纪的前十年,我们更是见证了个人计算机处理器的三次重大革命——64位处理器、多核心和高效图形处理器在个人电脑出现。在这样的背景下,乔布斯在 2008 年 WWDC(苹果全球开发者大会)上,宣布下一代 Mac 操作系统 Mac OS X 10.6 将被命名为 Snow Leopard(雪豹)来适应硬件架构的革新。就在那天下午,Bertrand Serlet 在一场开发者内部讲座上透露,和先前两个发行版包含大量的新功能(10.4 Tiger 包含 150 个新功能,10.5 Leopard 包含 300 个新功能)不同,Snow Leopard 不含任何新功能,仅是对 Leopard 中诸多技术的重大更新,以使其在现代架构上更稳定、高效。 在这十年的最后一年,2009年 8 月 28 日,苹果发布了 Mac OS X 10.6 来有效地支持这三项技术,而本文将为读者介绍其对应的三项软件技术——64位架构、Grand Central Dispatch,以及 OpenCL。 其他 Mac OS X 10.6 技术更新,如全新的 QuickTime X 和跳票的 ZFS,有着更复杂的历史背景(以后再为读者介绍)。
64位架构出现的缘由
前文提到,根据摩尔定律,同样价格的集成电路上可容纳的晶体管数目,约每隔 18 个月便会增加一倍,性能也将提升一倍。事实上,存储器的容量增长可能更快,每过 15 个月就会翻一番。有了更快更强的电脑,可能会让数值计算的科学家们喜出望外,但对普通大众来说,摩尔定律给普通消费者一个假象——如果你觉得 1000 美元的苹果电脑太贵,那等上 18 个月就可以用 500 美元买到同样的电脑。十年前你在用电脑写 Word 文档,十年后你还在用电脑写 Word 文档,反正计算机不是耗材,一台电脑只要不坏,就不用去买新的。计算机产业的巨头们自然知道摩尔定律对他们造成的致命打击,因此,一个阴谋被以 Intel 和 Microsoft 为首的巨头们构想出来——Intel 负责把硬件越做越快,而 Microsoft 则负责把自己的软件越做越臃肿、越做越慢——至于你信不信,反正我是信的。因此,使用软件、服务等,直接促进计算机产业的消费,使得计算机产业走上可持续发展的道路。这在计算机产业被称为 Andy-Bill 定律,分别以 Intel 和 Microsoft 总裁的名字命名。
当然,软件公司未必真心欺骗消费者,故意把软件做大做慢——为了实现一个新功能,软件势必会比原先庞大。但现代软件的速度、大小和其增加的功能并不成比例。比如对最终用户来讲,Windows Vista 到底比 Windows XP 多了多少功能呢?可能只有 20%~30%。Word 2007 对比 Word 2003 多了多少功能呢?可能也只有 20%~30%。但 Windows Vista、Word 2007 占用的 CPU、内存、磁盘空间,却比 Windows XP 和 Word 2003 翻了几番。究其原因,为了能赶快把新功能带给用户,我们不惜使用更方便但低效的编程语言(.NET、Java 等依赖虚拟机的语言就要比C慢许多,Python 等动态语言比C慢的不是一星半点)、快速开发(我们原先处理一个大文本,先分块,一点一点读到内存中,然后把处理完的部分写回磁盘,清空内存;而现在直接把它全读进来处理,开发方便,执行也快)。而用户必须为这些新功能买不成比例的单。64位就是在这个背景下迅速走入寻常百姓家的——程序占用越来越多的内存,而 32 位的寻址空间已不能满足软件运行的需要了。
64位 CPU 是指 CPU 内部的通用寄存器的宽度为 64bit,支持整数的 64bit 宽度的算术与逻辑运算。早在 1960 年代,64位架构便已存在于当时的超级电脑,且早在 1990 年代,就有以 RISC 为基础的工作站和服务器。2003年才以 x86-64 和 64 位元 PowerPC 处理器架构(在此之前是 32 位元)的形式引入到个人电脑领域。从 32 位元到 64 位元架构的改变是一个根本的改变,因为大多数操作系统必须进行全面性修改以取得新架构的优点。
成功的迁移
苹果向 64 位处理器的迁移花了整整 6 年时间,远长于该公司其他技术的迁移——向 Intel 的迁移仅用了一年时间,从经典 Mac OS 到 Mac OS X 也仅用了三年时间。总而言之,这场迁移是非常成功的:一方面,用户基本无痛苦,老的 32 位程序在目前最新版的 Mac OS X Lion 中依然可以完全兼容地执行;另一方面,对开发者而言,基本只需做微小的调整,重新编译程序,而且若干技术如 Universal Binary,使他们发布程序非常方便。当然,对于某些大量使用过时技术的公司,如 Adobe 和 Microsoft,这场迁移则要折腾得多。
这场迁移整整用了四个发行版的时间(10.3至 10.6),不同于 Windows 或 Linux,Mac OS X 对 64 位的迁移自下而上,再自上而下。先是内核扩展,逐渐上升至 Unix 空间,然后上升至用户界面,再上升至整个应用程序生态,最后完成内核的迁移。要提醒读者的是,Mac OS X 的 32 位和 64 位内核空间与用户空间的分配和实现,和 Windows 存在本质的区别,但在本期介绍中,我们尽可能少地把 Mac OS X 的 64 位迁移和 Windows 进行比较,不拘泥于技术细节,对此区别有兴趣的读者,请移步 AppleInsider 的系列专题。
2003年,苹果发布了其第一款 64 位计算机工作站 Power Mac G5。同期发布的 Mac OS X 10.3 也因此增加了非常简单的 64 位支援,于是 XNU 内核开始支持 64 位的寄存器和整数计算。但对于用户空间而言,程序可见的地址依然是 32 位的。程序当然可以使用大于 4GB 的内存(Power Mac G5 最高可达 8GB 寻址空间),但这要求程序手动地在两个 32 位内存空间中来回转换。
两年后,苹果发布了当时最成功的 Mac OS X 发行版 Mac OS X 10.4 Tiger。10. 4 的内核是革命性的,除了增加对内核并行多线程的支持,它把用户空间可见的地址空间扩展到了 64 位,因此理论上用户程序可以以 64 位方式执行。当然,在这个时期,几乎系统内的所有程序,哪怕是内核,依然是 32 位的。系统中唯一带的 64 位二进制文件是名为 libSystem.dylib 的系统库。它是 Mac OS X 上对C标准和 POSIX 标准的支持库,由 libc、libinfo、libkvm、libm 和 libpthread 五部分组成。但这仅有的 libSystem.dylib 理论上就能让所有仅使用C标准库和 POSIX 标准库的程序以 64 位模式运行。当时,用户对 64 位的需求较少,主要限于科学计算或图形处理等需要大数组的领域。因此,10.4能较好地满足这部分用户的需求。但如果程序需要调用除 BSD Unix 以外的系统调用,比如想用 Cocoa 来画图形界面,那么该程序仅能以 32 位方式运行了。对于一些需要 64 位寻址空间的科学计算程序,比如 Mathematica,就需要采用一些比较麻烦的做法:用一个进程调用 32 位的 Cocoa 画图形界面,用另一个进程调用 64 位的 libSystem 来进行运算和 Unix 系统调用,并用 Unix 管道或进程间通信的方式管理两个进程间的输入/输出。
苹果在 Mac OS X 10.4 发布同期的另一项重要决策是向 Intel 平台 x86 及 x86_64架构的迁移。为了帮助开发者和用户顺利迁移,苹果正式公布了 Universal Binary。Universal Binary 技术是 Mach-O 二进制文件早就具有的特性,只是在这个场合作为一个商业词汇进行宣传。NeXT 时代 NeXTSTEP 操作系统就支持许多种不同的硬件架构,自然可以要求开发者对每个平台发布一个独立的版本,但这样的分发模式很麻烦,消费者也需要搞清到底购买哪种平台的软件。因此 NeXT 的 Mach 内核所支持的 Mach-O 二进制文件格式引入了一种叫 fat binary 的特性,说白了就是在一个平台架构上分别交叉编译所有平台的二进制格式文件,然后把每个文件都打包成一个文件。Universal Binary 就是指同时打包 Intel 平台和 PowerPC 平台的二进制文件。Mac OS X 10.4 最终支持四个平台的 BSD 系统调用——32位 Power PC、64位 PowerPC、32位 x86 和 64 位 x86_64。作为最终用户,无须搞清这些区别,因为使用 Universal Binary 技术,买回来的软件直接会解出相应平台程序的二进制文件并执行。这是苹果很成功的一步——不像 Windows 系统中要用不同的路径(\Windows\System、\Windows\System32、\Windows\System64)分别存放不同架构的二进制库,并且用户还需在 32 位版和 64 位版之间犹豫不决。
Mac OS X 10.5 Leopard 经过一系列跳票终于在 2007 年末发布,跳票主要原因是当时苹果投入了大量人力和物力去做 iPhone,以至于 10.5 跳票了整整一年。10.5 包含了约 300 项新功能,而最重要的一项是苹果把对 64 位的支持带入了 Cocoa 层面。因此,几乎系统中所有的库都有四个平台的版本。在 WWDC 上乔布斯亲自向与会者介绍迁移到 64 位的好处,而能使用更大的内存自然是一项重要优势,程序可以申请更大的内存,把所有数据一并读入内存中操作,而无须分块后来来回回地在内存和磁盘搬运数据。另外,对 Intel 平台来说,x86架构只有 8 个寄存器,而 x86_64 平台有 16 个寄存器,这也就意味着,对该平台来说,只要重新编译程序,程序就能自由调度比原先翻倍的寄存器数量而无须快取或在内存中来回查找和读写。根据粗略估算,一般涉及大量数值计算的程序会加快一倍。所以他很开心地劝说所有的开发者都迁移到 64 位架构。
历时整整 6 年时间,苹果完成了向 64 位处理器的迁移,同时这也给苹果提供了良好的清理门户的机会——清理过时的技术和 API。
彻底的清理
同时,苹果做出了一个大胆的举动——Carbon 框架并未出现在这次迁移中。Carbon 是 Mac OS X 诞生之初为了帮助 Mac OS 开发者把老程序迁移到新的 Mac OS X 操作系统上所提出的一个兼容 API,这套 API 长得很像经典 Mac OS 的 API,但能够得到 Mac OS X 平台提供的一切新特性,Adobe、Microsoft 等都是通过 Carbon 把它们经典的 Mac OS 程序移植到 Mac OS X 上的。苹果的本意是希望开发者用 Carbon 迁移老程序,用 Cocoa 开发新程序,但在 Carbon 诞生之初,其受关注度远大于 Cocoa,据 TeXShop 开发者 Dick Koch 回忆,在 Mac OS X 刚诞生的开发者大会上,Carbon 讲座的教室挤满了人,而 Cocoa 相关的讲座上听者无几。维护两套雷同的 API 的代价自然很高,所以砍掉一个是大势所趋。Carbon 和 Java 的热度甚至一度让苹果产生索性把 Cocoa 或 Objective-C 砍掉的想法。大量苹果自家的程序如 Finder、iTunes、Final Cut、QuickTime 等也都是用 Carbon 写成的。不过在此后由于大量涌现在 Mac OS X 平台上的新程序都是 Cocoa 写的,导致 Cocoa 技术不断走高。2007年的 iPhone 也完全依赖于 Objective-C 和 Cocoa 的一个裁剪版 Cocoa Touch。因此在 WWDC2006 上,苹果在 Mas OS X Leopard 10.5 的开发预览版中包含了测试版本的 64 位 Carbon 库,甚至还有讲座教如何开发 64 位的 Carbon 程序。但苹果却在 2007 年告诉 Carbon 开发者,他们的程序将不可能再被编译成 64 位,要做到这点,必需先把程序用 Cocoa 重写。
这个突然的决定激怒了很多开发者,尤其是以 Microsoft 和 Adobe 这些巨头为代表的公司。Adobe 全套的 Creative Suite 和 Microsoft 全套的 Microsoft Office 是很多苹果用户必备的软件,数百万行代码全是用 Carbon 写的。所以直到今天,除了 Adobe Photoshop 等少数程序终于在 2010 年全面移植到 Cocoa 后做出了 64 位版,其他大部分程序依然停留在 Carbon 的 32 位模式。
苹果也花了很长时间来重写 Finder、FinalCut、iTunes、QuickTime 等程序或技术,耗费了大量精力。当 Adobe 发布 64 位的 Lightroom 2.0 时,苹果还在手忙脚乱地重写 Aperture。不过公正地讲,长痛不如短痛,砍掉对 Carbon 的支持能够使苹果把更多精力放在该做的事上,也使得 Mac OS X 的结构更简洁,并且事实上,64位的迁移为苹果提供一个砍去老 API 的机遇,哪怕对 Cocoa 也是。一方面,Cocoa 框架中很多类不是使用类似 Carbon 的 API,就是依赖于用 Carbon 实现(注意,和传统观念不同,Carbon 和 Cocoa 在早期 Mac OS X 上是相互依赖的,比如菜单 NSMenu 就使用了 Carbon 的菜单管理器),这些 API 在 64 位得到了彻底清理,QuickTime 相关的C接口全被砍去。Cocoa 经过很长时间的发展,自然也保留了很多过时的 API 以保证和原先的产品兼容,而这次机会给苹果足够的理由彻底推翻原先的设计。在 Mac OS X 10.5 中, Objective-C 的运行库 libobjc 更新到2.0,提供了全新的并发、异常处理、自动内存回收、属性(property)等新机制,其中很多新特性只供64位享用。同时,所有 int 都被改为 NSInteger,Core Graphics 中的 float 都改为 CGFloat,以保持 API 统一,这些都是 64 位架构上的改动。因此 64 位迁移给苹果一个很好的清理门户的机会。
作为相反的例子,这次清理也有不彻底的地方。比如从老版 Mac OS 中混进来的 Keychain 库,甚至具有 Pascal 风格的 API,由于没有替代品,它也得到了 64 位的更新。所以类似 keychain 这样的库成了现在 Mac OS X 程序员的噩梦。我每次用到 Keychain 都有痛不欲生的感觉。
而 2009 年发布的 Mac OS X 10.6 Snow Leopard 则是对 64 位真正完整的支持。Unix 层虽然 10.4 就提供了 64 位的 libSystem,但所有的 Unix 用户空间工具包括 ls、Python 等,以及 Xcode 中的 gcc,也都是以 32 位二进制的模式发布的。图形界面层,在 10.5 Leopard 中,虽然整个系统的库都迁移到 64 位,以 32 位和 64 位的混合模式发布,但用户应用程序依然是 32 位的。只有 Chess、Java、Xcode 套件等少数程序以 64 位编译。但在 10.6 中,基本所有的应用程序都被迁移到 64 位,不管是 Safari、Mail、Dock,还是 TextEdit。当然,各种 Unix 工具包括 LLVM、GCC 等也都以 64 位的模式发布。10.6只有四个 Carbon 程序(Front Row、iTunes、DVD Player 以及 Grapher)未得到 64 位升级【2009年查阅,现页面已更新至 10.7】。其中, Front Row 在 Mac OS X 10.7 Lion 中被砍掉, iTunes 在 10.7 发布时依然以 32 位模式发布,在 2011 年末的更新中才迁至 64 位。
为了使应用支持 64 位,苹果不遗余力地改写了大量代码,Snow Leopard 中最重要的重写当属 Finder,这个程序自 Mac OS X 发布以来就一直是一个 Carbon 程序,并且苹果一直不停地改进它以展示 Carbon 无所不能。但自从 10.5 时代苹果下决心砍掉 Carbon 后,该程序被完整地重写。新的 Finder 和 Carbon 版的 Finder 看上去并没有太大差别,但 Finder 使用 Cocoa 重写后,不仅速度更快,而且增加了许多 Cocoa 新特性,比如加入了更多的 Core Animation 特效来平滑过渡动画。总之,虽然苹果在 10.6 期间没有提供太多新功能,但这样大规模的重写,为今后代码的可维护性奠定了良好的基础。
Mac OS X 10.6 发行版也完成了 64 位化的最后一步——内核的 64 位化。我们将在下期杂志中和读者仔细讨论。
作者王越,美国宾夕法尼亚大学计算机系研究生,中国著名 TeX 开发者,非著名 OpenFOAM 开发者。
Mac OS X 背后的故事(一)力挽狂澜的 Ellen Hancock
Mac OS X 背后的故事(二)—— Linus Torvalds 的短视
Mac OS X 背后的故事(三)Mach 之父 Avie Tevanian
Mac OS X 背后的故事(四)—— 政客的跨界
Mac OS X 背后的故事(五)Jean-Marie Hullot 的 Interface Builder 神话
Mac OS X 背后的故事(六)上善若水
Mac OS X 背后的故事(七)上善若水下——Cordell Ratzlaff 引发的 Aqua 革命
Mac OS X 背后的故事(八)三好学生 Chris Lattner 的 LLVM 编译工具链