​聊聊 Python 优化机制之常量折叠

每种编程语言为了表现出色,并且实现卓越的性能,都需要大量编译器级的优化

一种著名的优化技术是 “常量折叠”(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)
(0)

相关推荐

  • 零基础小白Python入门必看——编程基础概念

    . 程序的构成 程序由模块组成,一个模块对应python的源文件 ,一般后缀为:.py 模块由语句构成 语句是python程序的构造单元,用于创建对象.变量赋值.调用函数.控制语句等. 2. 对象 每 ...

  • 理解lambda表达式,为什么用它?

    初学者接触 lambda表达式 ,觉得语法很清奇,正好今天看到 python weekly 推送了一个youtube的视频教程,今天大邓就顺便领大家简单认识一下lambda语法. up主:PyLeni ...

  • Python解释器哪个好?

    学会使用各式各样的工具是非常重要的,可以让工作或学习变得更加便利.高效.比如说Python,大家都知道Python是一门解释器语言,代码如果想要运行,必须通过解释器来执行,但是Python解释器众多, ...

  • RealPython 基础教程:Python 运算符和表达式

    " 运算是程序的基本功能,而运算符和表达式则是最基本的运算单元" 在了解了不同类型的变量之后,我们现在该用这些变量做点什么了. 今天,我们将了解如何在变量上执行计算.你最终将会掌握 ...

  • Python优化机制:常量折叠

    英文:https://arpitbhayani.me/blogs/constant-folding-python 作者:arprit 译者:豌豆花下猫("Python猫"公众号作者 ...

  • 最全总结 | 聊聊 Python 办公自动化之 Word(下)

    最全总结 | 聊聊 Python 办公自动化之 Word(下)

  • 嘘,Python 优化提速的 8 个小技巧

    作者:张皓 链接:https://zhuanlan.zhihu.com/p/143052860 Python 是一种脚本语言,相比 C/C++ 这样的编译语言,在效率和性能方面存在一些不足.但是,有很 ...

  • Python 优化提速的 8 个小技巧

    Python 是一种脚本语言,相比 C/C 这样的编译语言,在效率和性能方面存在一些不足.但是,有很多时候,Python 的效率并没有想象中的那么夸张.本文对一些 Python 代码加速运行的技巧进行 ...

  • 聊聊SQL优化的基础思路

    SQL优化是Oracle数据库中比较难的部分,需要对Oracle数据库具备非常扎实的理论基础.但是在刚开始接触时,往往不能很好地将理论知识应用到实践,或者有了一定的思路,又不自信或不敢确定是不是正确的 ...

  • 山东优化机制鼓励集采机构有序竞争

    栏目: 全国政采新闻联播,政采头条,电子报 时间:2021-01-18 18:09:08 [摘要] 贯彻落实"深改方案"·地方在行动 山东优化机制鼓励集采机构有序竞争 该省财政厅发 ...

  • python 包机制、导入模块

    表白:黑白圣堂血天使,天剑鬼刀阿修罗.  讲解对象:/python 包机制.导入模块 作者:融水公子 rsgz Python3 教程 python教程 http://www.rsgz.top/post ...

  • Python继承机制及其使用

    Python类的封装.继承.多态3大特性,前面章节已经详细介绍了Python类的封装,本节继续讲解Python类的继承机制. 继承机制经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上 ...

  • Python封装机制及实现方法

    不光是Python,大多数面向对象编程语言(诸如C++.Java等)都具备3个典型特征,即封装.继承和多态.其中,本节重点讲解Python类的封装特性,继承和多态会在后续章节给大家做详细讲解. 简单的 ...