写点什么

Python 优化机制:常量折叠

用户头像
Python猫
关注
发布于: 2021 年 02 月 12 日

英文: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 中。


例如,每当 astfoldexpr 遇到二值运算时,它便调用 foldbinop,递归地计算两个子操作对象(表达式) 。


fold_binop 函数返回计算后的常量值,如下面的代码片段所示:



foldbinop 函数通过检查当前运算符的种类,然后调用其相应的处理函数来折叠二值运算。例如,如果当前的操作是加法运算,为了计算最终值,它会对其左侧和右侧操作数调用 PyNumberAdd。


怎样优雅?


为了有效地折叠某些模式或类型的常量表达式,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)


发布于: 2021 年 02 月 12 日阅读数: 25
用户头像

Python猫

关注

Python猫作者,一个Python爱好者 2018.08.07 加入

历史文章会陆续搬运过来,欢迎大家在微信搜索“Python猫”,也可以找到我

评论

发布
暂无评论
Python优化机制:常量折叠