如果乔布斯会编程

Forklore.org 是一个相当不错的网站,专门收录第一代 Macintosh 设计期间的珍闻轶事。今天有件事情让我想到了里面的一篇:PC Board Esthetics

事情开始是吴雨鸿为修改一个 bug 写了一些代码并由整个 team 来 review。代码的算法很复杂。凭经验我感觉应该有更容易理解的方法。经过长时间的讨论,证明雨鸿的方法的复杂度也不是全无理由,因为原来程序的数据结构中有一个单向关联(unidirectional association)。如果用比较清晰的算法来写,需要蛮力遍历比较多的数据。(再次说明《Art of UNIX Programming》里面指出的数据结构清晰的重要性。)不过我认为在复杂算法和蛮力算法之间进行选择并不困难。When in doubt, use brutal force 应该是永恒的真理。

讨论 UNIX 原则和文化并非此文的主旨。问题在于,我突然遐想 Apple 的软件工程师在这种事情上会如何收场——特别是在 Steve Jobs 过问的情况下?在 PC Board Esthetics 里,Steve Jobs 激动地驳斥了一位工程师以用户不可见 PC 板为理由而放弃美学追求的意见——『高超的木匠不用糟木头做柜子背面!』 即使面对更为实际的电气方面的反对意见,Jobs 仍然坚持先按美观的方式做出来试试。结果 Macintosh 设计团队无怨无悔地多花了 5000 美元来证明美观的设计确实在电气方面存在难以克服的缺点。『退而』采用了朴素的设计。

硬件如此,软件又当如何?正如《In the Beginning It was the Command Line》里面说的,Bill Gates 自叹在品味上不如 Jobs,但是也有资本嘲笑 Jobs 不会编程。看来似乎 Apple 的软件工程师们可以大大地松一口气。可能也不尽然,Jeff Johnson 这位大牛在他的 blog 里介绍了脱离 XIB(以前叫 NIB)文件使用 Cocoa 开发的系列文章,在最后一篇里说 Jobs 来信说所有文章他都看过,明确承认大多数 Apple 的应用程序其实都是没有 NIB 的,还指出了哪些技术是 Apple 正在使用的。是个玩笑还是 Jobs 过人的精力再次给我们惊讶,谁知道呢?

如果 Jobs 会编程,Apple 的软件工程师会遭受什么样的折磨呢?就拿今天的例子来说。也许 Jobs 会笃信 UNIX 的格言,毫不犹豫的让手下把程序改成简洁的蛮力算法。也许 Jobs 凭借一贯的独特美学品位不会轻易接受蛮力算法,但是也绝对不会为了省去两个循环就允许工程师们用各种匪夷所思的手段去摆弄数据结构。他一定会要求一个可以理解的算法,并且用最生动的注释来说明。虽然是注释,排版也必需讲究,最好是能够形式地严格证明这个精巧的算法和蛮力算法在逻辑上等价。所以最后的结果可能正如那块折磨硬件工程师的 PC 板,软件工程师们在无怨无悔地多花了几天时间研究优美的算法是否可行之后重新采用蛮力算法。

4条回应 to “如果乔布斯会编程”

  1. fuzhou Says:

    在我看来,乔布斯的意见最终被否决的根本理由是电气性能确实达不到要求,由此可见乔布斯是讲道理的:在用户体验和美学方面,他在证据面前选择了用户体验。而其中5000美元的开销就是为了明白这一点必须付出的代价。至于他到底懂不懂设计,我认为这已经不是问题了。

    相比之下,我担心的一直是软件。在软件的世界里,我更多地看到的可不是自发地选择朴素的方案,相反地,很多同行们往往会主动地选择华丽的算法或框架。Windows的世界中这几乎俯拾皆是;即使在UNIX的世界里,我也仍然能见到这种貌似合理的逻辑在日益泛滥,比如Gnome。似乎在软件工程师的世界里,够用就好可不是美德。

    关于那个code review,我没参加过所以不好随便发表评论,但是我很好奇的是大家在review时有没有评估过蛮力算法可能的性能损失。另外,实际采用的复杂算法究竟节约了多少开销,有没有复杂度的估计?如果性能损失在实际测试中可以接受(比如设定一个标准,多大规模的数据应该在正负多少秒之内遍历完成),精美算法带来的维护和理解上的开销是不是值得?

    对了,作为一位爱因斯坦的崇拜者,我很想知道:如果一个算法本身让人难以理解,为什么我们会认为它精美?

  2. zy Says:

    我想乔布斯是基于他的审美理念要求硬件设计。那么为什么将被设计的对象转换成软件,乔布斯就变成了只注意排版注释和蛮力算法这样的表面东西了。『高超的木匠不用糟木头做柜子背面!』。那么软件的柜子背面是什么呢?

    算法的人为复杂性和问题天然的复杂性是不同的。当然我们在日常生活中见到了太多人为的复杂性。人们喜欢把自己的工作搞得复杂以显示价值。管理人员喜欢复杂的流程。但是有的问题自有其天然的复杂性,比如判断一个整数是否是素数,就不能用蛮力办法了。

    Wirth说了,程序=算法+数据结构 (以后我会经常引用这个公式,直到看到的人吐了为止)
    好的程序应该在算法和数据结构上达到的平衡。

    我以前也是爱因斯坦的崇拜者(现在我谁也不崇拜了,崇拜别人只会让我更傻),爱因斯坦说:Everything should be as simple as possible, but not simpler. 过于强调蛮力,在我看来,是在追求simpler。手里拿把剃头刀,到处乱砍。

  3. fuzhou Says:

    很难三言两语说明为什么我终于发现我和志岩老兄有取得一致的时候哪。虽然表面上看不出来。

    首先,我不是毫无原则地要求软件的设计应该简单简单再简单,所以也我同意算法的人为复杂性和天然复杂性不能一概而论。我希望强调的是:和天然复杂性相比,人为的复杂性是我们这些普通程序员们——或者说至少是我自己——每天都需要面对的,所以过度设计对我而言尤其值得警惕。

    也许这么说要犯众怒:我见过的程序员中没有蠢货,但是没有不是自大狂的。在真正需要分析和数据支持来做一个决策时,我们中的99%最终选择了拍脑门:因为我们够狂妄,习惯性地认为我们的设计不会有错,尽管事实上设计错误和编码错误同样致命。而另一个问题也许没那么引人注目:人的精力往往有限,在某个点上投入过度就难免在其他地方马虎大意,而这些看似无关紧要的小瑕疵最终会在某个时候突然跳出来,把我们打得人仰马翻。

    最后贴一个小故事,也许能帮助志岩老哥理解我在这个问题上为何如此神经过敏:

    去年我接手过一个关于程序死锁bug和一个crash的bug,它们都基于这样一个UI框架——没错,又是个框架:执行时它要求在Windows里插五六个全局钩子,并且替换comctl32.dll中至少三个以上控件的WndProc,实际执行时平均有超过30个线程并发,而且所有UI的焦点切换操作都需要在一个C++和一个.NET Service之间进行DCOM通信。这个框架核心部分接近5万行代码,没有一行log,甚至在Debug版本下也没有。我们接到bug的时候,用户那边发过来两个dump,打开一看,每个都超过60个线程。接下来一个月里Dev直接沉默了,只剩下一个Tester和四面八方解释。最后我成功地解决了第二个,而第一个则无法解决。

    后来我们找到最初写这个代码的人问为什么这段code要这么写,他们的回答让我们不知如何是好:因为他们认为这段代码的性能要求很高,为了保证性能,他们一致决定不写log。可为什么连debug版本都不写log?他们再也不回答了。

  4. fuzhou Says:

    顺便说一句,我所知道的是那个框架在第二个版本里被彻底重写:1. 加了一部分log,虽然很少,2. .NET的部分被C++的代码替换,相应地,DCOM通信被取消;3. 线程总数大约削减了一半,4. 最后也是最重要的:两个版本都没有性能能测试的计划。

    不知这能不能说明一点问题。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 注销 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 注销 /  更改 )

Connecting to %s


%d 博主赞过: