猛踩油门!令Python加速

先要能做 , 做得对 , 最后才是要做得快 。
——Kent Beck
这篇附录将致力于提供一些优化代码的工具 , 以便能使我们的代码变得更简洁 , 或者更快速 。尽管此类优化在大多数情况下并不能代替算法设计(特别是当我们所处理的问题规模非常大的时候) , 但是让我们的程序运行快上10倍应该还是能做到的 。
在调用外部辅助之前 , 我们应该首先审视一下自己是否已经做到Python的内置工具物尽其用了 。在本书中 , 我曾列举过许多类似的例子 , 其中包括了适用于双向队列的deque , 以及如何在合适的条件下运用bisect和heapq来提升算法的性能 。另外 , 作为一个Python 程序员 , 我们也很幸运Python提供了当今最高级也最为有效的排序算法(list.sort() , 并且高效地实现了它) , 以及一个功能多样而又迅速的散列表(dict) 。甚至您还会发现 , itertools与functools模块中也能给我们带来某种程度的高性能代码1 。
除此之外 , 当我们选择要用外部库来优化代码时 , 还应该先确认一下这种优化是否有必要 。优化本身也往往会让我们的代码变得更复杂 , 依赖关系更多 , 所以优化之前要想一想 , 优化是否真的值得 。如果您的算法已经“足够好” , 代码也“足够快” , 那么通常就不值得引入用其他语言(如C语言)撰写的外部模块了 。当然 , 什么是“足够好”、“足够快” , 也取决于我们自己的判断 。(您可以在第2章找到一些用于代码测速与剖析的例子 。)
需要注意的是 , 本篇附录中所讨论的包和外部扩展库主要用于优化单处理器的代码 , 其中既包括高效的函数实现 , 也包括封装好了的扩展模块 , 以及速度更快的Python解释器 。当然 , 考虑将我们代码改为多处理器版本确实能有助于大幅提高运行效率 , 所以您真的想这么做的话 , multiprocessing模块是一个好的开始 。如果您希望深入了解多核编程 , 您同样可以找到非常多的关于分布式计算的第三方工具 。例如 , 您可以查看一下Python wiki上Parallel Processing页面中的内容 。
在接下来的内容中 , 我们将会看到一份加速工具的选单 。其中包含了业界在若干个方向上的努力 , 应对于各种情境的变化 , 毕竟新项目会与时俱进 , 而一些旧项目则逐渐被淘汰 。如果您对下面所介绍的工具方案很感兴趣 , 打算将其运用到自己的项目来 , 我们建议您可以先浏览一下这些扩展库的网站以及社区——当然 , 这要根据您自己的需要 , 本篇附录最后附有这些网站的地址(见表A-1) 。
NumPy、SciPy、Sage与Pandas:NumPy是一个历史悠久的包 。它起源于Numeric和numaray这些更为古老的项目 。NumPy的核心是一个多维数字数组的实现 。除了该数据结构外 , 它还实现了若干个函数与运算符 , 它们能够高效地进行数组运算 , 并且对函数被调用的次数进行了精简 。您可以用它来进行极其高效的数学运算 , 而无需对内置模块进行任何额外的编译 。SciPy和Sage则属于那种更为宏大的项目 , 它们将NumPy内置为自身的一部分 , 同时内置了几种不同的工具 , 实现了用于特定科学、数学及高性能计算的模块(关于这些内容 , 我们在后面的内容中还会提到) 。Pandas是一个侧重于数据分析的工具 , 但只有等它的数据模块适用于我们的问题实例时 , 它才是一个强大而快捷的工具 。另一个与之相关的工具叫Blaze , 它在我们处理大量半结构化数据的时候是非常有用的 。
PyPy、Pyston、Parakeet、Psyco与Unladen Swallow:让代码运行得更快 , 且侵入性最小的方式之一就是使用实时编译器(just-in-time (JIT) compiler) 。在以前 , 我们可以用Python安装器来安装Psyco 。安装完成之后 , 我们就只需要直接导入psyco模块 , 然后调用psyco.full() , 代码的运行速度就会有明显的提升 。在您运行Python程序时 , Psyco会将我们的一部分代码编译为机器码 。而由于它可以在运行时监控程序 , 因此也就可以做出某些静态编译器无法做到的优化 。例如 , 一个Python list中可以包含任意类型的值 , 但当Psyco注意到该list其实只包含整型时 , 它就会假设 , 可能该list在之后的运行时间里也仅会包含整型 , 因而对相关代码进行编译 , 将该list编译为整型列表 。遗憾的是 , 如今包括Psyco在内的数个类似的Python加速器项目以及它们的网站都已经处于“停止维护以及消亡”的状态了 , 尽管类似的功能在PyPy中得到了传承 。


推荐阅读