移除 GIL,可顯著提升多線程性能么?

來源:開源中國
近日,一位名叫 Sam Gross 的開發(fā)者提出了一個對全局解釋器鎖(GIL)進行重大修改的設想。其目標在于移除 CPython 中的 GIL,以使得多線程能夠并行執(zhí)行 Python 代碼。目前,該項目已經(jīng)引起了 Python 核心開發(fā)團隊的關注。
我一直在對 CPython 進行修改,使其能夠在沒有全局解釋器鎖的情況下運行。我想與大家分享一個可以在沒有 GIL 的情況下運行的概念驗證。這個概念驗證涉及到對 CPython 內(nèi)部的大量修改,但對 C-API 的修改相對較少。它可以與許多 C 語言擴展兼容:擴展必須被重建,但通常只需要對源代碼進行較小的修改或不需要修改。我已經(jīng)從科學的 Python 生態(tài)系統(tǒng)中構建了兼容的軟件包版本,它們可以通過捆綁的"pip"來安裝。

Gross 在設計文檔中詳細列舉了移除 GIL 需要承擔的一些風險以及他認為要移除 GIL 的理由:
風險
移除 GIL 將是一項大型、多年的任務,引入錯誤和回歸的風險會增加。 移除 GIL 意味著在單線程和多線程性能之間進行權衡。“我相信 - 我們可以創(chuàng)建一個沒有 GIL 的 CPython,它在單線程和多線程工作負載方面都比當前的 CPython 更快;但是如果我們保留 GIL 并只專注于單線程工作負載,我們將實現(xiàn)更好的單線程性能”。(有關更多詳細信息,可參閱“Performance”部分。) 大多數(shù) Python 程序都沒有針對多線程性能進行優(yōu)化;他們可能需要更改以利用沒有 GIL 的運行。 多線程程序容易出現(xiàn)并發(fā)錯誤。盡管 Python 已經(jīng)支持線程(with the GIL),但成功移除 GIL 可能會增加線程的使用,從而導致 Python 程序員暴露于并發(fā)錯誤。
為什么要移除 GIL?
GIL 是并發(fā)的主要障礙。對于科學計算任務,這種缺乏并發(fā)性通常比執(zhí)行 Python 代碼的速度更重要,因為大部分處理器周期都花費在優(yōu)化的 CPU 或 GPU 內(nèi)核中。GIL 引入了一個全局瓶頸,如果其他線程調(diào)用任何 Python 代碼,它都會阻止其他線程取得進展。 在“Why Swift for Tensorflow”文檔中,Chris Lattner 寫道“GIL 在大規(guī)模設置中增加了整個堆棧的復雜性:它使模型作者無法為某些事情實現(xiàn)合理的性能(無需自己編寫 C++/CUDA),并阻止模型中的并發(fā)算法的自然表達。” 圍繞 GIL 進行了大量工程工作。這樣做的后果之一是,簡單地刪除 GIL 不太可能神奇地提高現(xiàn)有代碼的速度。但是 Gross 認為,移除 GIL 將可以更容易的利用并行性、簡化新庫的開發(fā)、并允許改進現(xiàn)有庫。
Gross 稱,自己進行概念驗證的目的是想要證明,取消 GIL 是可行的和值得的;且這個項目的技術想法可以作為這種努力的基礎。同時,他也想就這些想法和大家展開討論,并衡量社區(qū)對這種移除 GIL 的方法的興趣。
如果 Gross 的提議被接受,就會重寫 Python 在其運行時從多個線程訪問對象的序列化方式,并將顯著提高多線程的性能。這個項目對 Python 字節(jié)碼解釋器(ceval.c)進行了大幅修改;目的在于提高單線程性能,減少引用計數(shù)變化對性能的影響,以及提高多線程運行時的可擴展性。
測試結果表明,無 GIL 概念驗證的解釋器在單線程的 pyperformance 基準上比 CPython 3.9(和 3.10)快 10% 左右。截至 2021 年 9 月初,它的平均性能與 CPython 3.11 的"main"分支大致相同。在一些基準測試中,多線程性能實現(xiàn)了良好的提升。例如,在有 20 個線程的情況下,有一個基準測試中的任務速度提高了 18.1 倍,另一個基準測試中的任務速度則提高了 19.8 倍。
InfoWorld 認為,Gross 的提議包含了很大的更改,以至于相當數(shù)量的直接與 Python 內(nèi)部運作的現(xiàn)有 Python 庫(例如 Cython))都需要進行重寫。但是,Python 的發(fā)布時間表的節(jié)奏則意味著,這種破壞性變更需要在一個主要版本而不是次要版本中進行發(fā)布。

還不過癮?試試它們
