提升 Python 代码性能至接近 C 语言的速度,无需修改源代码。遵循 Python 之父吉多・范罗苏姆的建议:“如果你想让你的代码神奇地运行得更快,你应该试试用 PyPy。”
YouTube 视频链接:https://www.youtube.com/watch?v=2wDvzy6Hgxg&t=1012s
研究人员需要快速编写代码来验证想法的可行性,这对于他们的工作至关重要。Python 是一个理想的选择,它能帮助人们专注于想法本身,而无需过多关注代码格式等琐碎事务。然而,Python 的一个主要缺点是其运行速度相较于 C 或 C++ 等编译型语言明显较慢。
那么,当通过构建 Python 原型验证了想法后,如何将其转化为一个快速且高效的工具呢?通常情况下,人们需要进行额外的步骤,即手动将 Python 代码转换为 C 语言代码。但如果 Python 原型本身就能实现快速运行,那么转换代码的时间便可用于更具价值的工作。
PyPy 恰好能够解决这一问题,它能够显著提升 Python 代码的运行速度,甚至某些情况下可以与 C 语言相媲美。为了验证 PyPy 的性能优势,我们进行了以下实验:分别使用默认的 Python 解释器和 PyPy 来运行一段代码,该代码执行一个从整数 0 加到 100,000,000 的 for 循环,并打印出运行时间。以下是实验结果:
# 导入了 Python 的 time 模块。time 模块提供了各种与时间相关的函数,可以用来测量时间、处理日期时间等。
import time
# termcolor 是一个第三方库,用于在终端输出彩色文本。colored 函数可以根据指定的颜色来格式化文本。
from termcolor import colored
# 调用了 time 模块中的 time() 函数,并将返回值(当前时间的时间戳,单位为秒)赋值给变量 start。这里记录下开始计算之前的时间点,以便后续计算总耗时。
start = time.time()
# 初始化变量 number 为 0。这个变量将用来累加从 0 到 99,999,999 的所有整数。
number = 0
for i in range(100000000):
# 在循环体内,每次循环都将变量 i 的值累加到变量 number 上。这样,在循环结束时,number 将包含从 0 到 99,999,999 所有整数的和。
number += i
print(colored("FINISHED", "green"))
# 再次调用 time.time() 获取当前时间戳,并与之前记录的开始时间戳(存储在变量 start 中)相减,得到执行计算所花费的总时间(单位为秒)。然后,使用 f-string 格式化字符串,将计算出的耗时插入到字符串中并打印出来。这样用户就可以看到程序执行所需的时间。
print(f"Ellapsed time: {time.time() - start} s")
这不是学术评估,但结果令人惊叹。与默认的 Python 解释器(大约需要 10 秒)相比,PyPy 仅用 0.22 秒就完成了执行。更令人惊讶的是,无需任何修改,Python 代码即可直接在 PyPy 上运行。而在同一台计算机上,等效的 C 语言实现需要 0.32 秒。PyPy 甚至超过了最快的 C 语言实现。
为什么 PyPy 这么快?
尽管代码看起来完全相同,但其执行方式却大相径庭。PyPy 提升执行速度的关键在于采用 “即时编译”(Just-In-Time Compilation),也就是 JIT 编译技术。
C、C++、Swift、Haskell、Rust 等编程语言通常采用提前编译(AOT 编译)的方式。这意味着,使用这些语言编写的代码,在程序运行之前,编译器会将其源代码转换为特定计算机架构可执行的机器码。因此,当程序执行时,实际运行的是机器码,而非原始的源代码。
不同于 C 语言等上述语言,Python、JavaScript、PHP 等语言采用的是另一种方法 —— 解释器。与将源代码转换为机器码不同,解释过程中源代码保持不变。每次运行程序时,解释器都会逐行查看代码并执行。例如,每个 Web 浏览器都内置了 JavaScript 解释器。
PyPy 利用即时编译技术来执行 Python 代码,与传统解释器不同,它不会逐行运行代码,而是在程序执行前先将部分代码编译成机器码。即时编译结合了提前编译和解释的优点。如图所示,PyPy 采用的即时编译融合了这两种方法,通过提前编译提升性能,同时保持解释型语言的灵活性和跨平台可用性。
AOT 优点:
AOT 缺点:
JIT 优点:
JIT 缺点:
PyPy 在我们的程序已经很快或者大部分运行时间都用于调用非 Python 库时,效果较差。然而,如果我们有一个运行缓慢的程序,其中大部分时间都花在执行 Python 代码和密集的计算上,PyPy 可以发挥奇效。
总结:编译型编程语言会将源代码提前转化为机器代码,而解释型编程语言则由解释器逐行运行。即时编译技术结合了提前编译和解释的优点,实现边运行边编译。JIT 与 AOT 的主要区别在于编译时间:JIT 在运行时编译,而 AOT 则在程序运行前进行编译。PyPy 采用即时编译技术,显著提高了 Python 代码的运行速度。即时编译不仅提升了程序执行效率,还保持了解释型语言的灵活性和跨平台可用性。
📚️ 相关链接: