PYTHON · 深度实测
GIL没了?实测Python 3.13
花两天把Python 3.13自由线程从头跑到尾,结果有点意外——不是所有场景都变快了,有些反而更慢。帮你省掉踩坑的时间。
从Guido van Rossum在90年代引入GIL开始,Python社区就一直在吵要不要去掉它。GIL的作用很简单粗暴——同一时刻只允许一个线程执行Python字节码。
这个设计让CPython的内存管理变得轻松,代价是Python的「真·多线程」成了一个传说。你开了8个线程,CPU还是只用一个核,另外7个在旁边干瞪眼。multiprocessing能绕过去,但进程间通信的开销让很多人望而却步。
转折出现在2022年。Meta工程师Sam Gross提交了PEP 703,标题直截了当:Making the Global Interpreter Lock Optional in CPython。他的方案不是重写解释器,而是在现有CPython上做手术,把每个对象的引用计数换成原子操作。社区吵了两年,最终在2024年6月的语言峰会上投票通过。
2024年10月Python 3.13正式发布,自由线程模式作为实验特性面世。这里有一个关键信息:它不是默认行为。标准版Python 3.13的GIL还在,自由线程是一个独立的构建版本,需要你主动安装和使用。
macOS用户最省事,Homebrew已经收录了自由线程版本。装完之后你的系统里会多出一个`python3.13t`命令——注意末尾那个`t`,就是free-threaded的缩写。Linux用户可以通过源码编译,configure时加上`--disable-gil`参数。
如果你用的是pyenv管理Python版本,目前需要手动编译。先用`PYTHON_CONFIGURE_OPTS="--disable-gil" pyenv install 3.13`编译安装,之后用`pyenv global 3.13`切换。编译过程大约需要几分钟,取决于机器性能。
装好之后第一件事就是验证。打开`python3.13t`,打印`sys.flags.gil`。返回`0`说明GIL已经关闭,返回`1`就是还在。你也可以用`-X gil=0`参数或`PYTHON_GIL=0`环境变量在运行时切换,灵活性很高。
我用经典的素数计数写了一个基准测试。逻辑很简单:给定一个上限,用试除法统计素数个数。单线程跑4轮,和多线程同时跑4轮,对比耗时。
在标准Python 3.13里,多线程版本和单线程几乎一样慢——GIL让四个线程只能轮流执行,没有任何并行加速。这是我跑了无数次都确认过的结论。
切换到自由线程模式后,同样的代码给出了完全不同的结果。在我的M系列MacBook上(8性能核+8能效核),四个线程的素数计算时间大约是单线程的25%到30%。也就是说,接近4倍加速。
加速倍数跟CPU核心数正相关。两核的机器最多翻倍,四核接近四倍。但现实中你很难拿到理想的线性加速,因为内存带宽、缓存竞争、CPU调度都会拖后腿。不过即使打个七折,相比标准Python threading的「零加速」,这已经是质的飞跃。
以前Python threading完全做不到的事情,现在只差一个 `t` 后缀。
自由线程不是免费的午餐。
为了支持多线程安全访问,Python对象的引用计数从普通整数变成了原子操作。即使你只用单线程、完全不开多线程,这个额外的原子操作开销也一直在那里。
我的测试结果是:同样的素数计算,单线程模式下自由线程版本比标准版慢了大约5%到15%。这个范围取决于具体的操作类型——大量创建和销毁小对象的代码受影响更大,因为引用计数的原子操作频率更高。
单线程CPU密集 比标准版慢5%~15%(⚠️ 有代价)
多线程CPU密集 接近线性加速(✅ 大幅提升)
I/O密集多线程 基本持平(➡️ 无差别)
冷静判断 / 如果你是单线程代码,别指望自由线程能帮你提速——它只会让你更慢。
标准库在3.13里基本都适配了自由线程,问题出在第三方C扩展。
很多C扩展的代码里直接假设GIL存在——用GIL做隐式的线程同步,或者在获取GIL之前就操作Python对象。去掉GIL之后,这些假设全部失效,轻则数据竞争,重则段错误直接崩溃。
实测了一圈常用的库:NumPy可以正常import,基础数组操作没问题,但部分涉及内部状态的操作还有兼容性问题。pandas在我的测试中import就报错了,目前还没有完全适配。requests、httpx这类纯Python库则完全不受影响。scipy的情况跟NumPy类似——能用,但别指望所有功能都稳定。
建议你在考虑切换之前,先跑一轮兼容性检查。把项目依赖导出来,逐个在自由线程模式下`import`一遍。如果有C扩展的依赖,还要跑一遍核心功能测试。这比盲目升级然后在线上爆炸要好得多。
自由线程目前标记为实验性,但PEP 703的路线图是在3.14或3.15中转正。适合尝试的场景:
暂时别碰的场景:依赖大量C扩展的项目(尤其是pandas生态)、对单线程性能敏感的代码、I/O密集型应用(你本来就不用关心GIL)。另外,生产环境不建议使用——实验性功能意味着API可能变,行为可能调,出了问题社区支持也有限。
纯Python实现的CPU密集型多线程程序
从multiprocessing向threading迁移的项目(线程比进程轻量得多)
想提前踩坑为未来做准备的团队
LAST WORD
别急着卸载标准版
我的做法是装一个`python3.13t`在旁边,新项目拿它跑跑看,老项目暂时不动。等3.14稳定之后再认真评估迁移。
Python社区这次的方向是对的,但路还很长。你可以在评论区说说你打算什么时候升级——或者你觉得自己根本不需要去掉GIL?
方向对了,但路还很长。