关于 Python 3.13 你所需要知道的几点

简介: 关于 Python 3.13 你所需要知道的几点

什么是全局解释器锁 (GIL)?

自20世纪80年代末,Guido Van Rossum在荷兰阿姆斯特丹东部的一个科技园区开始开发Python编程语言,它最初被设计为一种单线程的解释型语言。这到底是什么意思呢?

你可能会听说,编程语言分为解释型和编译型两种。那么Python属于哪一类?答案是:它属于两者。

实际上,你很少遇到完全由解释器直接从源代码进行解释的编程语言。对于解释型语言而言,可读的源代码通常会被转换成一种中间形式,也就是字节码。然后,解释器会逐条读取这些字节码并执行指令。

这里,“解释器”通常被称作“虚拟机”,尤其是在Java等其他语言中,它们和Python一样,都是通过虚拟机来处理字节码。在Java等语言中,更常见的做法是直接分发编译后的字节码,而Python应用则通常以源代码的形式分发(尽管现在,包也经常以wheel和sdist的形式部署)。

这种意义上的虚拟机在许多意想不到的地方都会出现,比如在PostScript格式中(PDF文件本质上就是编译后的PostScript),以及在字体渲染过程中。

如果你在自己的Python项目中看到过很多*.pyc文件,这些就是你的应用程序编译后的字节码。你可以像探索Java类文件一样,对这些pyc文件进行反编译和研究。

当们执行Python程序时,Python的执行程序会生成一系列指令流,即字节码,然后解释器会逐一读取并执行这些指令。

如果你启动了多个线程,它们将共享相同的内存空间(局部变量除外),因此它们都能访问和修改相同的数据对象。每个线程都有自己独立的栈和指令指针来执行字节码。

如果多个线程同时尝试访问或修改同一个对象,比如一个线程在向字典中添加内容,而另一个线程在读取字典,这时你有两个选择:

  1. 让字典和其他所有对象都实现线程安全,这需要付出很多努力,并且对于单线程应用来说会降低效率;
  2. 创建一个全局互斥锁(通常称为mutex),这样任何时候都只有一个线程能够执行字节码。

后者就是们所说的全局解释器锁(GIL)。而前者是Python开发者提到的“自由线程”模式。

另外值得一提的是,GIL简化了垃圾回收的过程,使其变得更快速。由于垃圾回收本身是一个复杂的话题,们在这里不深入讨论,但简单来说,Python会跟踪一个对象被引用的次数,当引用计数降到零时,Python就知道可以安全地删除这个对象了。如果多个线程同时创建和删除对不同对象的引用,可能会导致竞态条件和内存损坏,因此任何自由线程的版本都需要为所有对象使用原子计数的引用。

GIL还简化了Python的C扩展开发(例如使用Cython),因为你可以在开发时做一些关于线程安全的假设,这会让你的工作更加轻松。

为什么会有全局解释器锁?

尽管Python近年来变得非常流行,但它并不是一门新语言——它在20世纪80年代末诞生,首次发布于1991年2月20日(比稍微早一点)。在那个时代,计算机的形态与现在大相径庭。大多数程序都是单线程的,单个CPU核心的性能正以摩尔定律所描述的那样呈指数级增长。在那种环境下,对于大多数不使用多核的程序来说,牺牲单线程性能以换取线程安全是没有必要的。

而且,实现线程安全显然需要付出很多努力。

这并不是说你不能在Python中利用多核CPU。只是说,你不能通过线程来实现,而必须通过多个进程(例如使用multiprocessing模块)。

多进程与多线程的主要区别在于,每个进程都有自己的Python解释器和独立的内存空间。这意味着多个进程无法访问内存中的相同对象,而必须使用特殊的机制和通信方式来共享数据(可以参考“在进程间共享状态”和multiprocessing.Queue)。

值得一提的是,与多线程相比,使用多进程会有更大的开销,而且在数据共享上也更加困难。

然而,使用多线程并不总是像人们通常认为的那样糟糕。如果Python正在进行I/O操作,比如读取文件或发起网络请求,它会释放GIL,让其他线程得以运行。这意味着,如果你的程序主要是I/O密集型的,那么多线程通常会和多进程一样高效。只有在你的程序是CPU密集型的,GIL才会成为一个显著的问题。

为什么想要去掉全局解释器锁?

虽然移除GIL的呼声已经存在多年,但主要阻碍并非因为工程量大,而是因为这可能会让单线程程序的性能受损。

如今,电脑的单个CPU核心的性能提升速度已经放缓(尽管像苹果硅芯片这样的定制处理器架构取得了巨大进步),而核心数却在不断增加。这就意味着程序越来越需要利用多核的优势,而Python在多线程利用上的不足也日益凸显。

到了2021年,Sam Gross开发了一个无GIL的原型,这激发了Python决策委员会提出并投票通过了PEP 703——将CPython中的GIL变为可选。投票结果是肯定的,决策委员会决定接受这个提案,并计划分三个阶段逐步实施:

第一阶段:自由线程模式作为一个实验性功能,不是默认启用的。 第二阶段:自由线程模式得到官方支持,但不是默认选项。 第三阶段:自由线程模式成为默认设置。 从讨论中们可以看出,大家普遍不希望Python分裂成两个版本——一个有GIL,一个没有——而是希望在自由线程模式成为默认后,GIL最终被完全移除,只留下自由线程模式。

在关于GIL的讨论进行的同时,还有一个名为“更快的CPython”的项目在并行推进。这个项目得到了微软的资助,由Mark Shannon和Python之父Guido van Rossum领导。

这个团队的努力已经取得了显著的成果,尤其是在Python 3.11版本中,性能相比3.10有了大幅提升。

有了社区和决策委员会的支持,多核处理器的普及,以及“更快的CPython”项目的成功,现在正是开始实施GIL可选化计划的第一阶段的好时机。

即时编译器(JIT,Just-in-Time)

在这个Python新版本中,除了全局解释器锁(GIL)的重大变革外,还引入了一个实验性的即时编译器(JIT)。

即时编译器(JIT)是一种编译技术,它在执行前即时生成机器代码,与传统的提前编译(AOT)方式,如C语言的gcc或clang编译器不同。

之前们已经讨论过字节码和解释器。关键点是,在Python 3.13之前,解释器会逐条处理字节码,将其转换成机器代码后再执行。而现在,有了JIT编译器,字节码可以一次性转换成机器代码,并在需要时更新,不必每次都重新解释。

需要强调的是,Python 3.13中引入的这种即时编译器是一种“复制和修补”类型的JIT。这是一种2021年新提出的概念,其核心思想是使用一系列预设的模板——JIT编译器会寻找匹配这些模板的字节码,并用预设的机器代码进行替换。

传统的JIT编译器在功能上要强大得多,同时对内存的需求也更大,尤其是与Java或C#这类重度使用JIT编译的语言相比。(这也是Java程序占用较多内存的原因之一。)

JIT编译器的美妙之处在于它们可以在代码运行过程中进行适应。例如,JIT编译器会追踪代码的“热度”,并根据代码的执行频率进行增量优化,甚至可以根据程序的实际运行情况来指导优化过程(类似于AOT编译器中的性能分析优化)。这意味着JIT编译器不会在只运行一次的代码上浪费时间优化,而是对频繁执行的代码部分进行重点优化。

目前,Python 3.13中的JIT编译器还比较简单,不会进行过于复杂的操作,但它为Python性能的未来发展带来了极大的期待。

JIT编译器将带来哪些改变?

在短期内,JIT编译器的加入可能不会影响你编写或执行Python代码的方式。但它是Python解释器内部工作机制的一个激动人心的变化,这可能会在未来显著提升Python的性能。

具体来说,这为逐步的性能改进打开了大门,这些改进可能会逐渐提升Python的性能,使其与其它编程语言更具有竞争力。尽管如此,由于目前仍处于初期阶段,复制和修补的JIT技术还很新且轻量级,因此在们开始从JIT编译器中获得显著的性能提升之前,还需要进行更多的重大改进。

如何尝试 JIT?

JIT 编译器在 3.13 中是“实验性”的,并且没有提供开箱即用的支持(至少当使用 pyenv 下载 3.13.0rc2 时)。您可以通过执行以下操作来启用实验性 JIT 支持:

$ PYTHON_CONFIGURE_OPTS="--enable-experimental-jit" pyenv install 3.13-dev
python-build: use openssl@3 from homebrew
python-build: use readline from homebrew
Cloning https://github.com/python/cpython...
Installing Python-3.13-dev...
python-build: use tcl-tk from homebrew
python-build: use readline from homebrew
python-build: use ncurses from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.13-dev to /Users/drew.silcock/.pyenv/versions/3.13-dev
$ python -c 'import sysconfig;print("JIT enabled 🚀" if "-D_Py_JIT" in sysconfig.get_config_var("PY_CORE_CFLAGS") else "JIT disabled 😒")'
JIT enabled 🚀

你可以在PEP 744的讨论页面上找到一些额外的配置选项(比如,要启用JIT编译器,需要在程序运行时加上 -X jit=1 参数等)。

这个测试只能确认JIT是否在编译时被启用,而不能判断它当前是否正在工作状态(例如,是否在运行时被关闭)。虽然可以在程序运行时检查JIT是否启用,但这稍微复杂一些。这里有一个脚本可以帮助你弄清楚这一点(来源于PEP 744的讨论页面):

import _opcode
import types


def is_jitted(f: types.FunctionType) -> bool:
    for i in range(0, len(f.__code__.co_code), 2):
        try:
            _opcode.get_executor(f.__code__, i)
        except RuntimeError:
            # This isn't a JIT build:
            return False
        except ValueError:
            # No executor found:
            continue
        return True
    return False


def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a


def main():
    fibonacci(100)
    if is_jitted(fibonacci):
        print("JIT enabled 🚀")
    else:
        print("Doesn't look like the JIT is enabled 🥱")



if __name__ == "__main__":
    main()

在PEP 744的讨论中,有人提到了PYTHON_JIT=0/1和-X jit=0/1这两种设置方式——测试后发现-X命令行选项似乎并没有什么效果,但是设置环境变量似乎能够达到预期的效果。

$ python is-jit.py
JIT enabled 🚀
$ PYTHON_JIT=0 python is-jit.py
Doesn't look like the JIT is enabled 🥱

总结

Python 3.13 版本带来了一些激动人心的全新概念和功能,这对 Python 的运行时环境来说是一个巨大的进步。虽然这些更新可能不会立即改变你编写和执行 Python 代码的方式,但可以预见,随着自由线程和即时编译器(JIT)技术的不断成熟和普及,它们将在未来几个月甚至几年内,逐渐提升 Python 代码的性能,尤其是对那些需要大量 CPU 计算的任务。

相关文章
|
6月前
|
存储 数据可视化 数据挖掘
python学习1补充
python学习1补充
|
6月前
|
Python
在Python Web开发过程中:`is`和`==`在Python中的区别是什么?
【4月更文挑战第25天】Python的`is`与`==`用于比较。`is`检查对象是否相同(内存地址一致),而`==`检查值是否相等。例如,`a = [1, 2, 3]`,`b = a`,`c = [1, 2, 3]`,则`a is b`和`a == b`均为True,但`a is c`为False,`a == c`为True,因`a`和`b`引用同一对象,而`a`和`c`值虽等但对象不同。
30 1
|
机器学习/深度学习 SQL 人工智能
这么多推崇学 Python 入 IT 行的,如果他们学完 Python 这一套找不到工作怎么办?
我觉得之所以有这样的担忧, 实则是对Python 这一套的学习体系可能理解的不到位哦, 众所周知随着互联网的飞速发展, 尤其是受到疫情的影响, 全球经济体系中的实体业逐年下滑, 在经济体系中独占优势的无外乎 金融行和互联网行业, 而在互联网行业中18年至今数据才是
这么多推崇学 Python 入 IT 行的,如果他们学完 Python 这一套找不到工作怎么办?
|
Python
python with as有什么好处?
python with as有什么好处?
75 0
|
算法 JavaScript Unix
怎么样才算是精通 Python?
程序员写过简历的都知道,先说精通->后说熟悉->最后说了解,要把精通放在最前面。 But 很少人会说自己精通Python,这也是因为Python的应用领域是真的多,最好还是说自己精通的领域,而不是语言,除非你真的是大佬。
254 0
怎么样才算是精通 Python?
|
API Python
十一将近,Python 带你分析哪里适合去
十一小长假将近,不知道各位小伙伴儿有没有想好去哪里玩呢。 虽然说每逢假期,国内的各大旅游景点都是人满为患,但是出去放放风还是有必要的,保不齐就能偶遇小小哥哥小姐姐呢! 既然是出去散心,那么天气状况就尤为重要了,下雨阴天,寒风凛冽的,可能不太好偶遇哦。下面就带着到家一起,看看十一期间,哪些地方的天气适合出(偶)游(遇)呢。
十一将近,Python 带你分析哪里适合去
|
JavaScript Java PHP
Python 入门篇-用Notepad++编写出第一个python程序
Python 入门篇-用Notepad++编写出第一个python程序
168 0
Python 入门篇-用Notepad++编写出第一个python程序
|
Python
Python 一行代码的神奇之处!!
你们知道今天聊聊关于 Python 一行代码的神奇之处!!!
120 0
|
Java C语言 Python
【Python面试】谈谈对 Python 和其他语言的区别?​
最近公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始,每天一题。如果参考答案不够好,或者有错误的话,麻烦大家可以在留言区给出自己的意见和讨论,大家是要一起学习的 。 废话不多说,开始今天的题目:
103 0