当前位置: 华文星空 > 知识

为什么 Python 的 GIL 问题一直让人诟病,Python 社区却不解决?

2020-07-27知识

介绍下最近公开的 Multithreaded Python without the GIL (下简称 nogil 项目). 这项工作由 FacebookAI 的 Sam Gross 投入了两年多时间开发,从公开后的反馈来看有希望在未来几年里真正进入 CPython. 这里基于公开资料以及和 Sam 的交流介绍下这个工作.

UPDATE 2023/01/10: 已经发PEP了! PEP 703 - Making the Global Interpreter Lock Optional in CPython

为什么 Facebook 会做这件事

Python 的 GIL 大家诟病已久就不多说了,但 Facebook 做这件事的原因其实跟 pytorch 有关. pytorch 由于主打 python-first, eager-first, 在性能上本来就吃亏, GIL的存在更是带来更多并行问题. 最明显的两点: (1) GIL 的存在导致 nn.DataParallel 性能垃圾 (2) GIL 导致多线程的 dataloader 没有太大意义.

这两个问题,目前 pytorch 都是用 multiprocessing 解决的, 但是随之而来的就是内存占用, context switch, IPC, 和 fork-safety 相关的无数的坑. 毕竟 fork is evil.

如何解决 GIL

从 Python 里拿掉 GIL 主要有两个大难题:

  1. 性能:

PyPy在这里 Frequently Asked Questions - PyPy documentation 提到两点原因: 第一是由于 python 主要基于 refcount 做 GC, 在如此动态的语言里做并行 refcount, 每个 object 都要 atomic refcount. 第二是 python 需要在用户写出 concurrency bug 的情况下保证解释器自己不崩,那么 mutable object 就几乎全都需要上锁. 这两点加起来导致每个 object 都会新增不少 overhead.

曾经的在 CPython 上移除 GIL 的尝试,包括 python-safethread, Gilectomy, 都因为性能太差失败了.

2. 兼容性:如果重写解释器,重新设计 GC 和 object model, 没有 GIL 不是不可能 (例如 Jython, pypy-stm). 但是 Python 语言最大的优势可以说就是它的生态,而 CPython 又是 Python 的事实标准,这些另起炉灶的项目目前没有一个流行起来.

而这次的 nogil 项目,首先是在 CPython 基础上尽量保持了兼容性: python 程序完全不受影响, 少量的 C API 有微小改动. 即使是 numpy 这种量级的项目也只需要改几行代码就能兼容.

在性能上, nogil 主要的亮点在于结合了多种 refcount 的方式降低 atomic 的 overhead: biased refcount, deferred refcount, immortalization; 把 allocator 换成了 thread-safe 并且能提供一部分 GC tracking 功能的 mimalloc.

在此基础上,把整个 CPython 解释器的所有部分都变得 thread-safe 也是很大的工作量.

目前的结果是,nogil 的单线程性能大约会丢失 10%. 为此,nogil 自带了一些独立无关的性能优化把单线程性能补救了一下,算是一个小 bonus. CPython 的单线程性能本来就还有很大优化空间,python 之父 Guido 在微软的团队甚至有一个 4 年 5 倍加速的宏伟计划, 因此 10% 的损失应该不算大.

至于多线程性能,nogil 在迷你 benchmark 上是能够线性提升的. 世界上目前还不存在可以用来 benchmark 的大型 Python 多线程应用,但是可以想象性能应该也是不错的.

nogil 项目的未来

这个项目大约一个月前公开,目前是受到 Python 之父和其他核心开发者的认可的. 这个工作量不可能很快并入上游,可能还会花一年甚至更多的时间,但是 CPython 维护者们已经表现出了积极合作的态度.

虽然在 API 上 nogil 尽量保持了兼容,但是删除 GIL 这个 semantics 的改变是不可能兼容的. 用户的 Python code 和 C extension 可能在失去了 GIL 的保护后会出 bug. 因此这个转变的过程会是缓慢且 opt-in 的, 几年内不太可能看到 CPython 会 nogil by default. 不过,未来的 CPython 只要提供一个 optional nogil mode, 应该就已经能革新整个 Python 社区了. 值得关注.