Python 解释器可以优化哪些东西?
Which things can the Python interpreter optimize?
我认为由于 惰性评估 语句,例如:
if True and True (...):
# do something
...应该在 Python 解释器的 True and
部分之后立即跳过。但是,与编译后的代码相比,我认为 Python 解释器不能像显式布尔比较那样优化 糟糕的风格 ,对吧?
if condition == True:
# do something
编译器会优化它并删除 == True
部分,但是解释器 总是 必须评估哪些语句在 condition ==
部分之后等待,因此每次执行代码时都进行不必要的 == True
比较?!
是否存在更多这样的陷阱,解释器无法优化代码?我知道最后一个问题很开放,但我想确实存在一些流行的例子?
它本身并没有太多的编译(CPython 确实编译成字节码),但是语言的极端动态特性阻碍了 "traditional" 优化,这是由难以检查的不变量引导的在 Python 的编译时,因为语言的语义使得有关代码实际应该做什么的大部分信息仅在 运行 时可用。
你的 if condition == True:
例子是一个很好的例子:只有当编译器能够证明 condition
总是一个布尔值时,比较才能被优化,如果它来自当前范围内除了文字之外的任何东西——如果它能证明,在 运行 时间,没有人设法用其他东西覆盖 True
(可能只在 Python 2 IIRC 中)。
请注意,某些类型推断是可能的,这实际上是 Jedi 等代码完成工具的工作方式;然而,Jedi 可以采取一些捷径并假设例如某种标准环境,而解释器必须应对语言允许的对全局变量的最奇怪的修改。
所以,一般来说,CPython 不会非常努力地优化任何东西 - 实际上,它似乎没有进行任何类型的 "smart" 代码分析,AFAICT 他们主要尝试构建一个高效但 "mostly vanilla" 的解释器。因此,期望几乎完全为您编写的内容付费,没有优化程序可以节省草率的代码。
顺便说一下,您的 and
示例不是解释器完成的优化,它只是运算符语义的定义方式(并且指定 and
和or
进行短路评估,它不是可选的 "optimization",因为如果右侧操作数有副作用,它可能会改变程序的可观察行为)。
动态语言的更好方法是 跟踪 JIT - 解释器让代码 运行 一段时间,并观察似乎成立的不变量(例如,condition
似乎总是一个布尔值,True
总是 True
,一些变量总是一个整数, ...),然后使用此信息将代码的优化版本编译为机器代码。这是 let 运行 只要上面的不变量成立 - 但是一旦用于编译优化版本的前提之一被破坏,执行就会回到解释模式,并且再次开始跟踪以找出是否可以构建一个新的优化版本。
因此,典型案例优化得很好,应用了通常的优化技术,但每当发生奇怪的事情(但语言允许)时,运行时间可以回退到常规 "literal"字节码解释。
这是用于从大多数动态语言中获得良好性能的方法(大多数现代 JavaScript 引擎使用这种基本方法的一些变体),并且在 Python 中用于 PyPy。
我认为由于 惰性评估 语句,例如:
if True and True (...):
# do something
...应该在 Python 解释器的 True and
部分之后立即跳过。但是,与编译后的代码相比,我认为 Python 解释器不能像显式布尔比较那样优化 糟糕的风格 ,对吧?
if condition == True:
# do something
编译器会优化它并删除 == True
部分,但是解释器 总是 必须评估哪些语句在 condition ==
部分之后等待,因此每次执行代码时都进行不必要的 == True
比较?!
是否存在更多这样的陷阱,解释器无法优化代码?我知道最后一个问题很开放,但我想确实存在一些流行的例子?
它本身并没有太多的编译(CPython 确实编译成字节码),但是语言的极端动态特性阻碍了 "traditional" 优化,这是由难以检查的不变量引导的在 Python 的编译时,因为语言的语义使得有关代码实际应该做什么的大部分信息仅在 运行 时可用。
你的 if condition == True:
例子是一个很好的例子:只有当编译器能够证明 condition
总是一个布尔值时,比较才能被优化,如果它来自当前范围内除了文字之外的任何东西——如果它能证明,在 运行 时间,没有人设法用其他东西覆盖 True
(可能只在 Python 2 IIRC 中)。
请注意,某些类型推断是可能的,这实际上是 Jedi 等代码完成工具的工作方式;然而,Jedi 可以采取一些捷径并假设例如某种标准环境,而解释器必须应对语言允许的对全局变量的最奇怪的修改。
所以,一般来说,CPython 不会非常努力地优化任何东西 - 实际上,它似乎没有进行任何类型的 "smart" 代码分析,AFAICT 他们主要尝试构建一个高效但 "mostly vanilla" 的解释器。因此,期望几乎完全为您编写的内容付费,没有优化程序可以节省草率的代码。
顺便说一下,您的 and
示例不是解释器完成的优化,它只是运算符语义的定义方式(并且指定 and
和or
进行短路评估,它不是可选的 "optimization",因为如果右侧操作数有副作用,它可能会改变程序的可观察行为)。
动态语言的更好方法是 跟踪 JIT - 解释器让代码 运行 一段时间,并观察似乎成立的不变量(例如,condition
似乎总是一个布尔值,True
总是 True
,一些变量总是一个整数, ...),然后使用此信息将代码的优化版本编译为机器代码。这是 let 运行 只要上面的不变量成立 - 但是一旦用于编译优化版本的前提之一被破坏,执行就会回到解释模式,并且再次开始跟踪以找出是否可以构建一个新的优化版本。
因此,典型案例优化得很好,应用了通常的优化技术,但每当发生奇怪的事情(但语言允许)时,运行时间可以回退到常规 "literal"字节码解释。
这是用于从大多数动态语言中获得良好性能的方法(大多数现代 JavaScript 引擎使用这种基本方法的一些变体),并且在 Python 中用于 PyPy。