Unladen Swallow 的失败与教训,以及关于 Pypy
曾经的希望
Unladen Swallow 是在2009年初开始进入 Python 用户视线的,它的出现一度引起了很多人的兴奋。
一是因为它诱人的目标,这个由来自 Google 的几个工程师发起的开源项目旨在大幅提高 Python 的性能 (5x),同时能还保持 Python 用户代码的兼容;
二是因为它的背景,Unladen Swallow 的开发人员都来自 Google,虽然他们宣称这个项目并不属于 Google 所有 (Google 只是赞助这些开发人员全职工作在该项目上),但众所周知 Python 是 Google 的官方语言之一,Google 内部使用了大量的 Python 代码,Google 本身也雇佣了大量的 Python 高手,其中就包括 Python 的发明者 Guido van Rossum 先生,这样的背景很难不让人对它的前景产生期望;
三是因为 Unladen Swallow 所选择的实现方法,Unladen Swallow 并不是从头去实现 Python,而是在 Python 2.6.1 上开出了一个新的分支,并将重点放在了那些 CPython 的性能瓶颈上,至于其它功能比如运行时库,内置函数,C 扩展等等则仍然使用 CPython 已有的代码。对于如何提高 Python 代码的执行速度,Unladen Swallow 的开发人员最主要的想法是为 CPython 添加一个基于 LLVM 的 JIT 编译器。由于 Unladen Swallow 的开发人员同时也是 CPython 的核心开发人员,再加上他们所选择的实现方法,使得 Unladen Swallow 的成果很有可能移植到 CPython 上,从而惠及所有 Python 用户。
不错的开始
Unladen Swallow 以一个季度为开发周期,在2009年一共发布了3个版本。
第一个版本 2009Q1 并没有对 CPython 做大的改动,只是一些小的性能相关的修订,根据发布说明给出的性能测试数据,2009Q1 比起 Python 2.6.1 有大约 25% ~ 35% 的性能提升。
第二个版本 2009Q2 引入了 LLVM 支持。开发人员声明该版本的目的主要是让 Python 能够在 LLVM 基础上工作,并为以后的性能优化打下基础。2009Q2 是令人兴奋的一个版本,因为通过引入 LLVM JIT 支持,所有的 Python 代码都可以编译成原生的机器码 (JIT可以动态编译那些调用次数多的函数,或使用-j always 选项强制编译所有代码),此外性能也比 2009Q1 提高了10%左右。美中不足的是内存的使用量增大了10倍。
第三个版本 2009Q3 原本是在 2009Q2 的基础上开始优化性能,但在遇到几个 LLVM 的问题后,开发人员只能暂时将这个计划搁在一边,不过该版本仍然令人兴奋,尤其是内存使用情况比起 2009Q2 来减少了930% (一个很令人困惑的数字,原因是 Google 采用了一个与众不同的百分比计算方法,即 (new – old) / new,而不是通常的 (new – old) / old)。
嘎然而止
在取得了不错的开始以及相当的关注后,备受期待的 2009Q4 版本,却迟迟没有推出。从2010年中开始,就不停的有人问 Unladen Swallow 是否已经死了。而直到现在,才一切真相大白。坦白的说,最根本的原因是因为开发人员无法实现当初为 Unladen Swallow 制定的目标 (将性能提升5倍)。这其中,又有各种不同的因素。
一是缺乏 Google 的支持,Unladen Swallow 虽然是开源社区共同参与的项目,但项目的主要开发人员却是在 Google 的赞助下,全职工作在该项目上,Google 希望通过提高 Python 的性能来优化内部使用的 Python 代码。然而在真正需要性能优先的地方,Google 仍然使用 Java 或 C++。Google 内部的其他 Python 开发人员,即使对 Unladen Swallow 感兴趣,在部署它时也面临着困难。去年11月份,两位核心的全职开发人员更是被 Google 调到了别的项目上,事实上宣布了该项目的结束。
二是缺乏社区的支持,在 Unladen Swallow 的开发上,主要以 Google 的工程师为主,缺乏其它主要社区人员;在 python-dev 上关于将 Unladen Swallow 合并到 Py3k 的反馈也颇多疑虑;此外,另一个与 Unladen Swallow 相像的项目 PyPy 则开始受到更多的关注,并成为 Python 社区的焦点,Unladen Swallow 的那些雄伟目标似乎已不再重要。
三是技术上的缺陷,Reid Kleckner 表示 LLVM 虽然提供了一个很好的编译器框架,但更像是为类似 C/C++ 这样静态编译的语言设计的,比如 LLVM 项目的亮点 optimization 就是专为 C/C++ 这样的语言编译生成的 IR 所设计的。此外 LLVM 本身也有不少限制。
当然关于 LLVM 是否真的不适合类似 Python 这样的动态语言还有争议,不过一个不争的事实是 LLVM 本身的优化大都工作在最底层,LLVM 不会去试图理解 Python 语言里那些动态特性,要想利用 LLVM,必须要先手工完成一些更高层级的 Python 的语言分析与优化。
Unladen Swallow 的遗产
Unladen Swallow 虽然失败了,但仍然留下了许多宝贵的遗产。Reid Kleckner 表示 Unladen Swallow 的性能测试工具包是其中最有价值的一个,现在被 PyPy 用于自己的性能测试。
Unladen Swallow 的开发人员还做了大量的针对 LLVM 的工作,比如为 LLVM 的 JIT 添加了 GDB 接口等等,这些工作大部分也都集成到了 LLVM 中去。
此外,某些 Unladen Swallow 已完成的性能优化工作,比如针对 pickle 模块的优化 (1, 2),也集成到了 Python 3.2 中。
下一站
提起 Unladen Swallow,就不得不提另一个项目 PyPy。PyPy 不是一个单纯的 Python 实现 (更像是一个编译器框架),它的主要目的也不是为了提高 Python 的性能 (性能只是 PyPy 众多优先级里的一个),但 PyPy 在自身发展的过程中,却正在实现 Unladen Swallow 当初所提出的宏伟目标,一个快速的 Python 实现。
理所当然的,在 Unladen Swallow 掩旗息鼓之后,快速发展的 PyPy 成为了 Python 社区的下一个热点。事实上,Unladen Swallow 的开发人员在当时并不是没有注意到 PyPy,只不过那时 PyPy 仍然有许多不足,比如速度慢,只支持 x86,不支持 C 扩展与 SWIG 等等。Unladen Swallow 的核心开发人员 Collin Winter 在谈到当时的选择时表示,如果选择 PyPy,那么将面临着许多非性能优化的工作,比如添加x86-64支持,添加C扩展以及SWIG支持等等,这些工作量将耗费他们很大的精力,而选择 LLVM 这条道则相对要容易的多,此外 LLVM 在当时比 PyPy 也稳定的多,至少苹果曾将 LLVM 的 JIT 用于自己的产品当中。
然而技术上的不确定性,却往往很容易的将当初的选择以及曾经的美好愿望绊倒,Unladen Swallow 的开发人员在利用 LLVM 迅速开始后,很快就遇到了问题。他们不得不花时间来解决 LLVM 自身的一些 bug,以及调整那些没用的功能。于此同时,PyPy 也在迅速的发展,他们添加了x86_64支持,添加了C扩展的支持,在性能上也有了长足的进步。这样的对比使得连 Unladen Swallow 的开发人员也对自己的项目失去了兴趣。
Unladen Swallow 开发人员的努力并不会因为项目的失败而白白流失,除了那些可见的成果外,他们还为我们留下了丰富的教训,有关开源项目的发展,有关技术上的抉择,有关具体的编译器开发的技术等等,至少我们希望后来者不再犯同样的错误,我们希望有一天 Python 真的能在性能上完成蜕变。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。