优化 Python 并不简单,因为这门语言本身非常灵活。下面是为什么即使优化有难度,它仍然是提升 Python 性能的最佳途径。 作者 | Serdar Yegulalp 翻译 | 苏宓 出品 | CSDN(ID:CSDNnews) 想让一个 Python 用户很生气?或许你只要说一句:“Python 很慢。” 在很多关键方面,的确如此。没有外部 C 语言编写的库的“纯”Python,在计算和处理对象时,远不如 C、C++、Java、Rust 或者 Go 那样快…… 为了应对这个问题,Python 用户长期以来采用了绕过“纯 Python”的方式。想要更快的数学计算?用像 NumPy 或 Numba 这样的库,或者用 Cython 把代码编译成 C。这些外部解决方案确实有效,而且 Python 社区中有一整套文化专门研究如何高效使用它们。 但这些“捷径”总是有代价的。用 NumPy 时,你的代码需要变得更抽象,不像其他语言那样细节丰富。用 Numba(或者某些情况下是 Cython)时,你只能用 Python 语言的一个小子集来表达你要做的事,并且这种方式能达到的速度是有限的。 总的来说,大家一直在呼吁一个问题:我们能不能让 Python 本身更快,开箱即用?多年来,答案逐渐变得清晰:“也许可以,但这很难。” 

为什么让 Python 变得更快这么难? Python 的性能和它是解释型语言这件事没有太大关系。最大的问题在于,Python 本身的“灵活性”——也就是它的动态性。 像 C++ 或 Rust 这样的语言,如果你给某个东西命名,编译器就能根据这个名称推断出你是什么类型的数据。编译器可以利用这种一致性,生成高效的代码来处理它。 但在 Python 中,这种保证不存在。一个命名的对象可以是任何东西。每次你要用这个对象时,必须去查找它的类型,再决定能做什么。因此,很多常见的优化根本做不了。 
为什么 Python 的类型提示救不了我们 Python 最近添加了类型提示功能,实际上在这里并不管用。它的目的是在代码运行前进行检查,而不是为了提高性能。其设计初衷就是让开发者提前检查代码的正确性,同时保持 Python 的灵活性。 那么为什么不创建一个使用类型提示来生成优化代码的 Python 方言呢?实际上,Cython 就有点类似。它能让代码更快,但只有在处理原生数据类型时,才能发挥较大作用。一旦你开始使用 Python 的对象类型(比如列表或字典),就得回到 CPython 运行时,性能就没那么好了。 不过,这个方法其实也不符合我们的初衷:我们不应该为了加速而离开 Python。所以问题就来了:能不能在 Python 内部搞定呢? 
Python 现在如何加速? 最近几个版本的 CPython(Python 的官方实现)引入了一些优化提议——有些是短期内可以实现的,有些则是远期目标。这些提议是 Python 开发者打算从内部加速 Python 的初步尝试。 
Python 的另一个替代实现——PyPy,使用即时编译(JIT)加速 Python。PyPy 通过生成机器本地代码来提升性能,但它是一个完全独立的 Python 实现,这就带来了维护、错误、兼容性等一系列问题。CPython 最近也在尝试引入 JIT 技术,但目前还没有带来显著的性能提升,主要是为将来的改进做准备。 另一个新变化是没有全局解释器锁(GIL)的 CPython 版本。GIL 是用来同步 Python 中多线程活动的,但这也限制了真正的并行执行。没有 GIL 的版本给多线程性能提升开辟了新天地,虽然它目前仍处于实验阶段。 这些只是正在推进的一些改进,未来还会有更多。我们可以看到,没有一种技术能立刻让 Python 变得飞快。加速 Python 的关键是尽量让解释器少做一些事情,比如避免类型检查,减少引用计数等。 
为什么加速 Python 必须在 Python 内部完成 在这些优化出现之前,曾有人提出这样一个想法:与其让 Python 变得更快,或者开发一个新的 Python 运行时(比如PyPy),不如开发一个与 Python 高度兼容的新语言,并逐步将 Python 用户迁移到这个新语言上。 这就是 等项目的做法。Mojo 有 Python 类似的语法,甚至在一定程度上支持 Python 的代码兼容性。但与 Python 不同,Mojo 默认编译成机器本地代码。 不过,这种语言并不能完全替代 Python,在需要回退到 Python 兼容性时,性能优势就会丧失。任何语言,无论是 Mojo 还是其他类似项目,都很难实现与 Python 现有生态系统的完全兼容,或者获得 Python 已经拥有的广泛接受度和用户基础。 所以,Python 本身应该成为最好的替代品。这个目标可能只能一步步实现,但这种迭代过程让现有的 Python 社区能够与语言一起发展,而不是从头开始重新创造。某些改进(比如去掉 GIL 的 Python)会比其他改进(比如类型专门化)更具影响力,但最重要的是创新不断涌现。 接下来,就看 Python 社区如何利用这些创新,推动 Python 不断向前发展。
|