當前位置: 妍妍網 > 辦公

深扒一個​Python最佳化機制:常量折疊

2024-02-27辦公

原文: https://arpitbhayani.me/blogs/constant-folding-python

作者:arprit

譯者:豌豆花下貓(「Python貓」公眾號作者)

聲明:本轉譯是出於交流學習的目的,基於 CC BY-NC-SA 4.0 授權協定。為便於閱讀,內容略有改動。

每種程式語言為了表現出色,並且實作卓越的效能,都需要大量編譯器級的最佳化。

一種著名的最佳化技術是「 常量折疊 」(Constant Folding):在編譯期間,編譯器會設法辨識出常量運算式,對其進行求值,然後用求值的結果來替換運算式,從而使得執行時更精簡。

在本文中,我們將深入探討什麽是常量折疊,了解了它在 Python 世界中的適用範圍,最後解讀 Python 的原始碼(即 CPython),並分析出 Python 是如何優雅地實作它。

常量折疊

所謂常量折疊,指的是 在編譯時 就尋找並計算常量運算式,而不是 在執行時 再對其進行計算,從而會使執行時更加精簡和快速。

>>> day_sec = 24 * 60 * 60

當編譯器遇到一個常量運算式時,如上所述,它將對運算式求值,並作替換。

通常而言,運算式會被「抽象語法樹」( Abstract Syntax Tree,簡寫為 AST)中的計算值所替換,但是這完全取決於語言的實作。

因此,上述運算式可以等效地被執行為:

>>> day_sec = 86400

Python 中的常量折疊

在 Python 中,我們可以使用 反組譯模組 (Disassembler)獲取 CPython 字節碼,從而更好地了解程式碼執行的過程。

當使用 dis 模組反組譯上述常量運算式時,我們會得到以下字節碼:

>>> import dis
>>> dis.dis("day_sec = 24 * 60 * 60")
0 LOAD_CONST 0 (86400)
2 STORE_NAME 0 (day_sec)
4 LOAD_CONST 1 (None)
6 RETURN_VALUE

從字節碼中可以看出,它只有一個 LOAD_CONST ,以及一個已經計算好的值 86400

這表明 CPython 直譯器在解析和構建抽象語法樹期間,會折疊常量運算式 24 * 60 * 60,並將其替換為計算值 86400。

常量折疊的適應範圍

Python 會嘗試折疊每一個常量運算式,但在某些情況下,即使該運算式是常量,但是 Python 並不會對其進行折疊。

例如,Python 不會折疊 x = 4 ** 64 ,但會折疊 x = 2 ** 64

除了算術運算式,Python 還會折疊涉及字串和元組的運算式,其中,長度不超過 4096 的字串常量運算式會被折疊。

>>> a = "-" * 4096# folded
>>> a = "-" * 4097# not folded
>>> a = "--" * 4096# not folded

常量折疊的內部細節

現在,我們將重點轉移到內部的實作細節,即關註 CPython 在哪裏以及如何實作常量折疊。

所有的 AST 最佳化(包括常量折疊)都可以在 ast_opt.c 檔中找到。基本的開始函式是 astfold_expr,它會折疊 Python 源碼中包含的所有運算式。

這個函式以遞迴方式遍歷 AST,並試著折疊每個常量運算式,如下面的程式碼片段所示:

astfold_expr 在折疊某個運算式之前,會嘗試折疊其子運算式(操作物件),然後將折疊操作代理給特定的運算式折疊函式。

特定操作的折疊函式對運算式求值,並返回計算後的常數,然後將其放入 AST 中。

例如,每當 astfold_expr 遇到二值運算時,它便呼叫 fold_binop,遞迴地計算兩個子操作物件(運算式) 。

fold_binop 函式返回計算後的常量值,如下面的程式碼片段所示:

fold_binop 函式透過檢查當前運算子的種類,然後呼叫其相應的處理常式來折疊二值運算。例如,如果當前的操作是加法運算,為了計算最終值,它會對其左側和右側運算元呼叫 PyNumber_Add。

怎樣優雅?

為了有效地折疊某些模式或型別的常量運算式,CPython 不會寫特殊的邏輯,而是呼叫相同的通用程式碼。例如,在折疊時,它會呼叫通用的 PyNumber_Add 函式,跟執行常規的加法操作一樣。

因此,CPython 透過確保其通用程式碼/計算過程可以處理常量運算式的求值,從而消除了編寫特殊函式來處理常量折疊的需要。

參考材料

  • 常量折疊 (https://en.wikipedia.org/wiki/Constant_folding)

  • CPython最佳化(https://stummjr.org/post/cpython-optimizations/)

  • Python dis模組與常量折疊(https://yasoob.me/2019/02/26/python-dis-module-and-constant-folding/)

  • CPython實作常量折疊的簡單方法(https://utcc.utoronto.ca/~cks/space/blog/python/CPythonConstantFolding)

  • AST的常量折疊最佳化過程(https://bugs.python.org/issue1346238)

  • Crossin的新書【 碼上行動:用ChatGPT學會Python編程 】已經上市了。 本書以ChatGPT為輔助,系統全面地講解了如何掌握Python編程,適合Python零基礎入門的讀者學習。

    購買後可加入讀者交流群,Crossin為你開啟陪讀模式,解答你在閱讀本書時的一切疑問。

    Crossin的其他書籍:

    添加微信 crossin123 ,加入編程教室共同學習 ~

    感謝 轉發 點贊 的各位~