沈崴 - 為什么 Python GIL 是一個(gè)杰出的設(shè)計(jì)

CPython 有 GIL 是因?yàn)楫?dāng)年設(shè)計(jì) CPython 的人偷懶嗎? ① —— 簡(jiǎn)單的答案是:不僅沒有偷懶,相反 GIL 是一個(gè)杰出的設(shè)計(jì)。

一冕杠、Greg Stein 的嘗試

Guido van Rossum 提到 ② ,在 1999 年酸茴,Greg Stein(及 Mark Hammond ?)曾嘗試開發(fā)過一個(gè)無 GIL 的 Python(據(jù)信是 1.5 版)分支分预,該分支對(duì)“所有變量”施以細(xì)粒度線程鎖。這讓 Python 的單線程性能下降了兩倍薪捍,這足以抵消多線程所能為 Python 帶來的性能提升笼痹。

這次嘗試讓 Python 在取消 GIL 這件事上變得非常謹(jǐn)慎配喳,GIL 得以保留至今。但是反過來說凳干,我們發(fā)現(xiàn) Guido 在最初所選擇的 GIL —— 其實(shí)已經(jīng)是最優(yōu)方案了晴裹。

二、GIL 為什么快

拿數(shù)據(jù)庫來進(jìn)行類比纺座,使用粗粒度的 GIL 就好象在操作數(shù)據(jù)時(shí)息拜,直接鎖定整個(gè)數(shù)據(jù)庫溉潭。SQLite(非 WAL 模式)就是這樣一種直接鎖庫的實(shí)現(xiàn)净响,同時(shí) SQLite 也是在單線程下最快的主流 SQL 數(shù)據(jù)庫喳瓣。

使用細(xì)粒度鎖來代替 GIL馋贤,類似于鎖定到每個(gè)字段。由于過于影響性能畏陕,數(shù)據(jù)庫鎖定粒度一般不會(huì)做到這么細(xì)的程度配乓。而對(duì)于 Python 解釋器來說,由于很難從用戶代碼中抽離出類似于數(shù)據(jù)庫“行惠毁、表”級(jí)別的代碼段犹芹,來進(jìn)行中等粒度的鎖定,所以也就無法做到在不大量損失性能的情況下鞠绰,來去除 GIL 了腰埂。

我們?cè)趯?shí)際開發(fā)中,經(jīng)常會(huì)遇到類似的粒度選擇問題蜈膨。有經(jīng)驗(yàn)的開發(fā)者習(xí)慣上會(huì)采用一些“簡(jiǎn)單粗暴”的方法來解決問題屿笼,雖然看上去這很初級(jí),但是這樣做卻是最安全翁巍、穩(wěn)定驴一,和高效的。相反許多初學(xué)者反而會(huì)采用很多復(fù)雜灶壶、“高端”的方法來解決問題肝断,這些代碼往往都不夠安全、穩(wěn)定驰凛,同時(shí)又很低效孝情。

事實(shí)往往和表面上看到的不太一樣。對(duì)于有經(jīng)驗(yàn)的開發(fā)者來說洒嗤,簡(jiǎn)單粗暴的大規(guī)模數(shù)據(jù)鎖箫荡、GIL 等方案,其實(shí)是經(jīng)常會(huì)被用到的渔隶。這往往是一種下意識(shí)的選擇羔挡,背后是有豐富的開發(fā)經(jīng)驗(yàn)來支撐的洁奈,并不是偷懶的結(jié)果。

三绞灼、PyPy 的嘗試

PyPy 使用 STM(Software Transactional Memory)來去除 GIL利术,這有點(diǎn)像 Python 的 ZODB 數(shù)據(jù)庫基于 Conflict 的并發(fā)機(jī)制那樣,是一種無鎖實(shí)現(xiàn)低矮。但是印叁,STM 目前不適用于兩種場(chǎng)合 ③ :

  1. 大量的 I/O 阻塞
  2. 大量的運(yùn)算在 C Extensions 中進(jìn)行

相反 GIL 對(duì)這兩種場(chǎng)合非常在行。事實(shí)上并行 I/O 是多線程主要解決的問題军掂,并行計(jì)算在 Python 中又常常是交給 C Extensions 處理的轮蜕,所以 CPython GIL 在實(shí)際使用時(shí),在并發(fā)性能上蝗锥,并不會(huì)輸于無 GIL 的 PyPy STM跃洛。這再次證明了 GIL 是一個(gè)相當(dāng)杰出的設(shè)計(jì)。

四终议、我的方案

順便說一下汇竭,如果讓我來設(shè)計(jì) Python,要支持多線程的話穴张,我一定會(huì)采用 GIL细燎。但是多線程就一定是必須的嗎?事實(shí)上在我看來皂甘,多線程并不是一個(gè)很好的并發(fā)模型玻驻。在需要多核進(jìn)行并行計(jì)算的時(shí)候,我們可以采用更安全的多進(jìn)程模式叮贩,在需要高并發(fā) I/O 的時(shí)候击狮,我們則可以選擇更加安全、高效的“異步”和“協(xié)程”模式 —— 如果不使用多線程益老,我們自然就可以去除 GIL 了彪蓬。

所以,我啟動(dòng)了一個(gè)叫做“C10K ④ ”的項(xiàng)目捺萌,通過 DLL Injection 在不修改用戶程序的情況下档冬,將線程自動(dòng)替換成協(xié)程,這能把任意語言編寫的程序都變成異步程序桃纯,并且順便把 Python 的 GIL 給去掉了 —— 這也是我目前認(rèn)為酷誓,最漂亮的去除 GIL 的方法了。我在視頻「標(biāo)準(zhǔn)線程的協(xié)程替換 ⑤ 」中做了詳細(xì)講解态坦。

五盐数、GIL 出現(xiàn)的歷史原因

Python 開始于 1989年 12月,為 Amoeba 操作系統(tǒng)(一種分布式 POSIX 系統(tǒng))設(shè)計(jì)伞梯,當(dāng)時(shí)是小型機(jī)時(shí)代玫氢,而 PC 機(jī)則處于 386/486 時(shí)代帚屉。雖然在 90 年代我在國(guó)內(nèi)的科研單位已經(jīng)看到有堆疊了大量 CPU 的工程機(jī)(今天叫做眾核機(jī)),但是“真正意義上的”多核(對(duì)稱雙 CPU 方案漾峡,SMP)實(shí)際商用的產(chǎn)品 ⑥ 出現(xiàn)于 1999 年攻旦。而最早的單一多核 CPU ⑦ 出現(xiàn)于 2000~2001 年。也就是說生逸,在 Python 出現(xiàn)的那個(gè)時(shí)代牢屋,多核尚未實(shí)質(zhì)性出現(xiàn)。

既然沒有多核槽袄,那么也就不存在“基于多核多線”的“并行計(jì)算”了烙无。由于當(dāng)時(shí) Unix 程序員習(xí)慣上是使用多進(jìn)程來處理多任務(wù)的(進(jìn)程在 Unix 中很輕,尤其是內(nèi)存在進(jìn)程間是通過“寫復(fù)制”來共享的)掰伸,所以多線程在單核時(shí)代的“唯一用途”是配合多進(jìn)程皱炉,在進(jìn)程中并行化處理阻塞式 I/O 用的怀估。

由于多線程會(huì)對(duì) CPU 產(chǎn)生競(jìng)爭(zhēng)狮鸭,在單核時(shí)代,線程開得越多多搀,系統(tǒng)性能就會(huì)越低歧蕉。所以在 Python 出現(xiàn)的那個(gè)年代,如果要提升程序性能康铭,正確的做法并不是去增加活躍的線程數(shù)量惯退,相反應(yīng)該把活躍線程數(shù)減少,以降低 CPU 競(jìng)爭(zhēng)从藤。既然在“單核時(shí)代”線程并沒有“多核并行計(jì)算”的用途催跪,僅僅是處理阻塞式 I/O 用的,那么提升性能最好的方法就是:把整個(gè)程序鎖住夷野,讓程序中只能有一個(gè)活躍線程 —— 這就是 GIL懊蒸。

最后我用一個(gè)極端假設(shè)來說明這個(gè)問題:“假設(shè)可以在沒有任何性能損耗的情況下去掉 Python 的 GIL”—— 那么在單核時(shí)代,GIL CPython 還是會(huì)比 GIL-free CPython 性能更高 —— 所以站在當(dāng)時(shí)的角度來看悯搔,只能說 GIL 干得漂亮骑丸。

六、GIL 的真正問題

GIL 并不影響 I/O 并行妒貌,在需要多核并行計(jì)算的時(shí)候通危,CPython 會(huì)通過 C Extensions 和多進(jìn)程來解決問題,因此 GIL 對(duì)并行計(jì)算的影響其實(shí)經(jīng)常不大灌曙。多進(jìn)程的 IPC 損耗也沒有那么泛化菊碟,主要是集中在與共享內(nèi)存相關(guān)的 ⑴ 高頻內(nèi)存共享 ⑵ 大內(nèi)存共享 —— 這兩個(gè)場(chǎng)景中。這時(shí)經(jīng)過權(quán)衡在刺,GIL 對(duì)性能的影響“也許”會(huì)超過多進(jìn)程操作臨界資源所帶來的安全性好處逆害,這最終會(huì)影響到程序開發(fā)的自由度藏古。GIL 阻礙了程序的“按需并行 ⑧ ” —— 這才是更本質(zhì)的問題。


① 整理自知乎上的探討 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
⑤ 沈崴 - 標(biāo)準(zhǔn)線程的協(xié)程替換 - PyCon China 2020 視頻版 https://www.bilibili.com/video/BV1ir4y1c7Dw 文字版 http://www.reibang.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/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忍燥,一起剝皮案震驚了整個(gè)濱河市拧晕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梅垄,老刑警劉巖厂捞,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異队丝,居然都是意外死亡靡馁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門机久,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臭墨,“玉大人,你說我怎么就攤上這事膘盖‰食冢” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵侠畔,是天一觀的道長(zhǎng)结缚。 經(jīng)常有香客問我,道長(zhǎng)软棺,這世上最難降的妖魔是什么红竭? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮喘落,結(jié)果婚禮上茵宪,老公的妹妹穿的比我還像新娘。我一直安慰自己瘦棋,他們只是感情好稀火,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著兽狭,像睡著了一般憾股。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箕慧,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天服球,我揣著相機(jī)與錄音,去河邊找鬼颠焦。 笑死斩熊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伐庭。 我是一名探鬼主播粉渠,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼分冈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了霸株?” 一聲冷哼從身側(cè)響起雕沉,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎去件,沒想到半個(gè)月后坡椒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尤溜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年倔叼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宫莱。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丈攒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出授霸,到底是詐尸還是另有隱情巡验,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布绝葡,位于F島的核電站深碱,受9級(jí)特大地震影響腹鹉,放射性物質(zhì)發(fā)生泄漏藏畅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一功咒、第九天 我趴在偏房一處隱蔽的房頂上張望愉阎。 院中可真熱鬧,春花似錦力奋、人聲如沸榜旦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溅呢。三九已至,卻和暖如春猿挚,著一層夾襖步出監(jiān)牢的瞬間咐旧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工绩蜻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铣墨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓办绝,卻偏偏與公主長(zhǎng)得像伊约,于是被迫代替她去往敵國(guó)和親姚淆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容