當前位置: 華文星空 > 知識

為什麽 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 社區了. 值得關註.