Archive for 2011年1月

Prime Ridant

2011/01/26

Asimov《基地》系列的主线『心灵史学』(psychohistory)是不可能的科学。Asimov 年轻时受热力学启发构思了『心灵史学』。他自己后来坦承,热力学的简单分子运动和人的行为毕竟有天壤之别,如果稍稍年长一些可能就没有勇气仅凭这么牵强的启发构想如此宏大的科学。

步入老年之后,Asimov 开始创作《基地》三部曲的前传。第一部《 Prelude to Foundation 》描写 Seldon 还是一个从边远星系来到银河帝国首都的年轻数学家,虽然提出了『心灵史学』的概念但是自己都不相信它能成为预言的实用科学。守护人类的机器人 R.Daneel 用了几个月帮助他建立了把这门科学推向实用的决心,而那之后十多年的时间里尽管有 R.Daneel 作为帝国首相的全力协助,『心灵史学』的实用化进展甚微。

前传第二部《 Forward the Foundation 》中的一个转折点之后,『心灵史学』的实用化开始稳步前进。这个转折点是 Prime Ridant 的发明。通过 Prime Ridant ,所有参与『心灵史学』研究的数学家能够随时地持续地对其全部公式进行修改和审查。『心灵史学』的实用化程度逐步达到可以被称为『 Seldon 工程』的地步。

我当初读到这段的时候觉得 Asimov 描述的情节太过唐突。难道在 Seldon 困惑了十几年之后令『心灵史学』的研究一下子步入正轨的只是一个审查和存储工具?我记得,当时我把这段重新读了一遍,然后放下小说。过了几天,又读了一遍。这次我看到 Asimov 描写『心灵史学』的公式体系远远比现实中的数学复杂,突然豁然开朗 —— 当面对的复杂度超过某个程度时,高效稳定的持续集成和审查工具确实能起到让工作发生质变的作用。从复杂度来说,Asimov 笔下的『心灵史学』已经不太像数学,而是更类似今天的软件代码。所以一切明朗了:Prime Ridant 就是一个 version control 系统,拥有海量的存储,稳定的并发访问能力,方便的审查功能,以及优秀的用户界面(三维投射)。从小说的一些描写还能看出,Prime Ridant 还提供不错的 branching 功能,因为还未经过严格审查的修改只能进入相对不稳定的分支。

我不知道 Asimov 是否了解软件开发。即使他对软件开发的任何知识都闻所未闻,我也不会觉得把 Prime Ridant 和 version control 联系到一起有丝毫的牵强附会。在《 What Techology Wants 》一书中,《连线》的创始人已经说明了人类社会的技术和思想在并行独立发展中的自发汇聚现象:三个学者同时接近完成进化论(虽然达尔文第一个发表),20 个人同时独立接近发明电灯,非欧几何被至少三个数学家同时独立构建,三个物理学家同时接近完成相对论,等等。Asimov 作为一个对技术有独到见解的名家,在设想人类如何构建高度复杂系统的时候,描写的虚构工具和 version control 系统有众多相似之处,这种思想的汇聚本身就说明了 version control 在软件开发中的本质核心地位。

OpenGL 随想(三):同步与多线程

2011/01/21

去年十月写过一篇《并行执行》,讨论了多核与锁的问题。提到了『并行执行』和『并发访问』两个概念。最近看公司里面老外写的文档,也用了同样的概念,不同的名称,分别叫做『性能化多线程』和『功能化多线程』。根据公司里项目的实践,『并行执行』除了像《并行执行》里讨论的向 GPGPU 转化之外,也在向 Intel TBBMKL 之类的 non-lock-based 技术转化。锁这个技术,在『性能化多线程』中的应用已经逐渐萎缩,并且我认为『功能化多线程』也会为了降低复杂度而逐渐向 timer 等 cooperative 并发技术转移。

本质上说,OpenGL 也是『性能化多线程』。这种多线程的特点是所有线程都不共享数据,所以无需中间同步。只需要最后的结果同步一次。类似主线程对所有性能化线程调用 join 。OpenGL 有两个操作可以提供这种 join 的语义,glReadPixels()glFlush() 。这两个操作都是为了防止用户看到数据(渲染结果)的不合法状态。比如 glFlush() 经常作为 glutSwapBuffers() 的隐含步骤执行。

来自代码

2011/01/18

舍弃旧代码是程序员经常面对的一种诱惑。不必说维护旧系统的人经常要面对,就是开发新功能新产品的人也时常如此 —— 因为你经常被建议要借鉴一个老产品的 code base ,而你本能地想拒绝而重新打造一个干净的系统。幸运的是,很早以前我已经开始相信舍弃这些东西是不对的,不应该重写任何不必重写的东西。当几次看到那些险些被舍弃的旧东西节省了大量工作之后,我更是坚信对那种看起来又老又旧的东西,应该留出一个星期的时间来仔细研究它是否有用。这一个星期可能会节省一个月的工作。

这个原则后来读到的很多文章里得到印证。但是我记得开始编程的时候完全不是这么想的。最近一个星期我仔细回顾了一下这个转变过程。

2005 年开始看 Linux kernel 的代码。我当时有一种稍稍自卑的感觉,感慨 Linux kernel 的代码为什么能把一切情况都考虑周全。比如一个 40 行左右的函数,让我来写最多只能写出十几行,而差的那 30 行处理的是很重要又不容易考虑到的情况。

当时看代码是用《 Understand the Linux Kernel, 3rd Edition 》做主线。书上讲的是 2.6.x 版本( x 是一个远小于 11 的数字)。而我参照的源代码是从网上下载的最新稳定版。从 2.6.1x 一直到 2.6.2x 更新了很多次。其间越来越多的看到书上的代码和最新代码的差异。跟随这些差异我开始阅读不同版本的 release notes ,以及针对特定修改的 mail list 讨论和 patch 注释。最后我意识到,书上的版本和最新版本之间的差异,往往就类似于上面提到的我能写出的十几行和实际的 40 多行的差异。为什么能把一切情况都考虑到?答案就是持之不懈地 Fix Bug 。

回想起来,尽管不够清晰,但这应该就是『绝不重写无必要重写的代码』这个想法产生的过程。没有人能把事情一次做对。旧的代码有很多错误,但是抛弃它们只是让已经犯过的错误有机会被重犯一次,加上新的错误。

另一方面,那些最后形成的代码,如果不比对《 Understanding the Linux Kernel, 3rd Edition 》上的旧版代码,似乎像是一次写就的。这是我最初的自卑的来源,也是很多初学者对编写代码的错觉。人们其实并不像最后结果体现的那样能一次就把事情做对,但是我们对错误的修正也没有必要直接体现错误本身。在循序渐进的过程中要把结果造就成一次就做对的面貌。如果你真的想回忆每个与之战斗过的错误,version control 才是怀念历史的地方。

已经有三年多没有碰过 Linux kernel 代码了。回顾起来,阅读它的代码让很多正确的想法第一次被植入到我头脑中。对于程序员来说,更多正确的有用的东西还是来自代码。

技术的洁癖

2011/01/11

看到 Twitter 上讨论 Mac 的单文件应用的卸载问题。首先说明一下,单文件应用是 Mac 上一种很常见的发布方式,整个应用只有一个文件(其实是一个目录),安装即拷贝该文件到 /Applications 下,卸载即删除这个文件。Mac 下还有很多种其它发布方式,比如 Xcode 使用的是 Apple 推荐的一种多文件 installer ,Photoshop 是 Adobe 自己设计的 installer ,而且 Mac OS X 也不会限制任何人发明定义自己的安装方式。这里只讨论单文件应用。

精确的说,单文件应用的安装实际发生在第一次启动,这时应用会按照需要在用户的数据目录下创建必要的配置文件。所以,简单的删除应用文件这种卸载方式其实确实会留下一些垃圾。

这个问题给了两种人不同的反应。第一种人是一定要把 OS X 的单文件卸载和 Windows 下流行的反安装(uninstall)比较的人。我想分两个层面讨论这种反应。首先,Windows 的反安装鼓励软件发行者安装的时候做了什么卸载的时候就反过来做什么;而 Mac 的单文件应用卸载鼓励发行者置少量垃圾于不顾。从理想层面说 Windows 是占优的。可是实际上,没有一个 Windows 应用的反安装能完全兑现理想,甚至很多应用连自己的 DLL —— 也就是可执行文件都删不掉。Windows 和 Mac 的方案相比较,前者是一个完全兑现就能美好的不得了,但是稍有差池就跌下地狱的方案,后者是一个基本能 100% 兑现但是天然有轻微缺陷的方案。

第二个层面是要追寻卸载应用的动机。自从买过上一台 MacBook 到现在用这台 MacBook Pro ,我从来没有卸载过一个应用。原因很简单,硬盘够用,系统也没有变慢。用 Windows 的时候我也几乎不用反安装功能,因为卸也卸不干净;而且一般三个月左右,最多四个半月系统就慢到必须重装的地步了。

被这个问题引发兴趣的第二种人是宣称自己喜爱整洁的人。对他们来说,就算系统不变慢,硬盘还有 2T 空间,只要想到还有几个 k 的垃圾文件隐藏在边边角角就浑身不得劲。对于这种把『喜好』、『就是』作为原因的人,仔细从人性和人类发展方面思考一番总是能得到些乐趣。比如说,装修摸墙面的时候都得把墙面刮花。如果一个人坐在整洁的客厅,想到墙面下其实是刮得乱七八糟的墙体而百爪挠心呢,是不是比较分裂?还有今天的文件系统早就不是那个一周用 defrag 整理几次的 FAT16 系统了,它会按照自己的工作把文件随意摆放。喜好整洁的人是否也不乐意呢?Joel 曾经看到过一台『肮脏至极』的面包机,而事后证明那是制造美食的好工具。其实在维系人类正常发展的效率需求之前,洁癖是没有生存空间的。

最后声明一下,我不认为 Mac 的方案是完美的。更进一步,不论是 Windows 的安装/反安装加注册表,Mac 的单文件应用加不完全卸载,还是 Linux 的集中化包管理,都有很大的改进空间。只是所谓基于『卸的干净』的洁癖这种思考方式太过简单了。

分裂的代价

2011/01/01

当 Microsoft 刚刚推出 Direct3D 的时候,PC 还是一个弱小的平台,接受 OpenGL 这样的工作站级别标准很遥远。所以『单独搞一套』似乎是天经地义的。当 NT 平台和 Windows 95/98 逐渐合并的时候,工作站级别的 OpenGL 和 Direct3D 功能重叠的问题开始被关注。最后,Microsoft 退出 OpenGL ARB 说明了它的决定 —— 用私有标准来 lock-in 开发者的 code base 和 knowledge base 。

经过了近十年之后,Direct3D 已经成为 3D 领域中一个功能上受人尊敬的竞争者,但是其分裂初衷实现得如何呢?去年年初,游戏大牛 David Rosen 写了 《 Why You Should Use OpenGL and not DirectX 》,说明在游戏开发界有众多严肃的开发者坚持开发基于 OpenGL 的产品,其中部分甚至是只在 Windows 上开发也不涉及 OpenGL 之外的接口。而在 Rosen 文章之下的评论里不乏反对的声音,其中一个理由是:只要软件的结构清晰,从 Direct3D 转向 OpenGL 代价并不高,可以说除了资源的确捉襟见肘的 just startup 都能应付。两种声音说明,无论是对于有强烈主观倾向的开发者,还是对于重视实用可操作的开发者,lock-in 都并不成功。

对 Microsoft 来说,进一步展示抛弃 OpenGL 的决心,也许会让开发者三思。所以出现了 Vista 剔除 OpenGL 的言论。但是事实是,Microsoft 不能在大量的现有 code base 的压力下冒天下之大不韪。它仍然必须提供与 Direct3D 同样完美的 OpenGL 支持。

Dirtect3D 让 Microsoft 拥有一个完全可控的标准。但是这种可控度并非在 OpenGL 上不可达到。OpenGL 和 Java,HTML 这类标准不同。作为一个对功能覆盖十分克制的专业标准,OpenGL 天然的必须和平台相关的代码混用。作为 native code 的源代码级别标准,OpenGL 天然的必须重编译才能跨平台。所以 OpenGL 的开发者对于私有扩展和细节差异的容忍度要远远高于 Java/HTML 之类开发者。为了平台可控性而创立 Direct3D 的必要性是值得质疑的(当然平台可控并非 Microsoft 的全部目的)。

应该说 Direct3D —— 尤其是 DirectX 11 的版本 —— 是一个优秀的开发平台。但是对微软来说,现状是这样的:无法抛弃 OpenGL,所以必须维护两套并行的接口,加大了维护成本。对于可控性的收益来说,投入大大高于必要。Lock-in 开发者 code base 的目标基本失败。对于平台的创立者来说,这是一个教训:应该对 JVM 这种基于 VM 的跨平台方案保持距离,但是对 OpenGL 和 POSIX 这样宽松的源代码级接口进行分裂是毫无必要的。(附带说一句,Windows 的 POSIX 子系统面临类似的程度稍轻的窘境。)