沈崴 - 为什么 Python GIL 是一个杰出的设计

CPython 有 GIL 是因为当年设计 CPython 的人偷懒吗? ① —— 简单的答案是:不仅没有偷懒,相反 GIL 是一个杰出的设计。

一、Greg Stein 的尝试

Guido van Rossum 提到 ② ,在 1999 年,Greg Stein(及 Mark Hammond ?)曾尝试开发过一个无 GIL 的 Python(据信是 1.5 版)分支,该分支对“所有变量”施以细粒度线程锁。这让 Python 的单线程性能下降了两倍,这足以抵消多线程所能为 Python 带来的性能提升。

这次尝试让 Python 在取消 GIL 这件事上变得非常谨慎,GIL 得以保留至今。但是反过来说,我们发现 Guido 在最初所选择的 GIL —— 其实已经是最优方案了。

二、GIL 为什么快

拿数据库来进行类比,使用粗粒度的 GIL 就好象在操作数据时,直接锁定整个数据库。SQLite(非 WAL 模式)就是这样一种直接锁库的实现,同时 SQLite 也是在单线程下最快的主流 SQL 数据库。

使用细粒度锁来代替 GIL,类似于锁定到每个字段。由于过于影响性能,数据库锁定粒度一般不会做到这么细的程度。而对于 Python 解释器来说,由于很难从用户代码中抽离出类似于数据库“行、表”级别的代码段,来进行中等粒度的锁定,所以也就无法做到在不大量损失性能的情况下,来去除 GIL 了。

我们在实际开发中,经常会遇到类似的粒度选择问题。有经验的开发者习惯上会采用一些“简单粗暴”的方法来解决问题,虽然看上去这很初级,但是这样做却是最安全、稳定,和高效的。相反许多初学者反而会采用很多复杂、“高端”的方法来解决问题,这些代码往往都不够安全、稳定,同时又很低效。

事实往往和表面上看到的不太一样。对于有经验的开发者来说,简单粗暴的大规模数据锁、GIL 等方案,其实是经常会被用到的。这往往是一种下意识的选择,背后是有丰富的开发经验来支撑的,并不是偷懒的结果。

三、PyPy 的尝试

PyPy 使用 STM(Software Transactional Memory)来去除 GIL,这有点像 Python 的 ZODB 数据库基于 Conflict 的并发机制那样,是一种无锁实现。但是,STM 目前不适用于两种场合 ③ :

  1. 大量的 I/O 阻塞
  2. 大量的运算在 C Extensions 中进行

相反 GIL 对这两种场合非常在行。事实上并行 I/O 是多线程主要解决的问题,并行计算在 Python 中又常常是交给 C Extensions 处理的,所以 CPython GIL 在实际使用时,在并发性能上,并不会输于无 GIL 的 PyPy STM。这再次证明了 GIL 是一个相当杰出的设计。

四、我的方案

顺便说一下,如果让我来设计 Python,要支持多线程的话,我一定会采用 GIL。但是多线程就一定是必须的吗?事实上在我看来,多线程并不是一个很好的并发模型。在需要多核进行并行计算的时候,我们可以采用更安全的多进程模式,在需要高并发 I/O 的时候,我们则可以选择更加安全、高效的“异步”和“协程”模式 —— 如果不使用多线程,我们自然就可以去除 GIL 了。

所以,我启动了一个叫做“C10K ④ ”的项目,通过 DLL Injection 在不修改用户程序的情况下,将线程自动替换成协程,这能把任意语言编写的程序都变成异步程序,并且顺便把 Python 的 GIL 给去掉了 —— 这也是我目前认为,最漂亮的去除 GIL 的方法了。我在视频「标准线程的协程替换 ⑤ 」中做了详细讲解。

五、GIL 出现的历史原因

Python 开始于 1989年 12月,为 Amoeba 操作系统(一种分布式 POSIX 系统)设计,当时是小型机时代,而 PC 机则处于 386/486 时代。虽然在 90 年代我在国内的科研单位已经看到有堆叠了大量 CPU 的工程机(今天叫做众核机),但是“真正意义上的”多核(对称双 CPU 方案,SMP)实际商用的产品 ⑥ 出现于 1999 年。而最早的单一多核 CPU ⑦ 出现于 2000~2001 年。也就是说,在 Python 出现的那个时代,多核尚未实质性出现。

既然没有多核,那么也就不存在“基于多核多线”的“并行计算”了。由于当时 Unix 程序员习惯上是使用多进程来处理多任务的(进程在 Unix 中很轻,尤其是内存在进程间是通过“写复制”来共享的),所以多线程在单核时代的“唯一用途”是配合多进程,在进程中并行化处理阻塞式 I/O 用的。

由于多线程会对 CPU 产生竞争,在单核时代,线程开得越多,系统性能就会越低。所以在 Python 出现的那个年代,如果要提升程序性能,正确的做法并不是去增加活跃的线程数量,相反应该把活跃线程数减少,以降低 CPU 竞争。既然在“单核时代”线程并没有“多核并行计算”的用途,仅仅是处理阻塞式 I/O 用的,那么提升性能最好的方法就是:把整个程序锁住,让程序中只能有一个活跃线程 —— 这就是 GIL。

最后我用一个极端假设来说明这个问题:“假设可以在没有任何性能损耗的情况下去掉 Python 的 GIL”—— 那么在单核时代,GIL CPython 还是会比 GIL-free CPython 性能更高 —— 所以站在当时的角度来看,只能说 GIL 干得漂亮。

六、GIL 的真正问题

GIL 并不影响 I/O 并行,在需要多核并行计算的时候,CPython 会通过 C Extensions 和多进程来解决问题,因此 GIL 对并行计算的影响其实经常不大。多进程的 IPC 损耗也没有那么泛化,主要是集中在与共享内存相关的 ⑴ 高频内存共享 ⑵ 大内存共享 —— 这两个场景中。这时经过权衡,GIL 对性能的影响“也许”会超过多进程操作临界资源所带来的安全性好处,这最终会影响到程序开发的自由度。GIL 阻碍了程序的“按需并行 ⑧ ” —— 这才是更本质的问题。


① 整理自知乎上的探讨 https://www.zhihu.com/question/439920631/answer/1685766305
② It isn't Easy to Remove the GIL https://www.artima.com/weblogs/viewpost.jsp?thread=214235
③ Software Transactional Memory https://doc.pypy.org/en/latest/stm.html
④ C10K Plan https://github.com/wilhelmshen/c10k
⑤ 沈崴 - 标准线程的协程替换 - PyCon China 2020 视频版 https://www.bilibili.com/video/BV1ir4y1c7Dw 文字版 https://www.jianshu.com/p/c3e1b60d8eaf
⑥ ABIT BP6 https://en.wikipedia.org/wiki/ABIT_BP6
⑦ Power4 The First Multi-Core, 1GHz Processor https://www.ibm.com/ibm/history/ibm100/us/en/icons/power4/
⑧ Why Is GIL Worse Than We Thought? https://laike9m.com/blog/why-is-gil-worse-than-we-thought,140/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容