这种内部 Python 优化如何用于数学表达式?
How does this internal Python optimization work for mathematical expressions?
这是一道优化题。我在函数中有一个表达式,如下所示:
>>> def x():
... num = 2 * 4 * 100 * 20
...
>>> x.__code__.co_consts
(None, 2, 4, 100, 20, 8, 800, 16000)
表达式2 * 4 * 100 * 20
的结果是16000
,所以如果我们反汇编x
:
>>> dis.dis(x)
2 0 LOAD_CONST 7 (16000)
3 STORE_FAST 0 (x)
6 LOAD_CONST 0 (None)
9 RETURN_VALUE
16000 几乎是所需要的。 co_consts
存储 8 和 800 技术上不再需要我们有总数还是它们?
将上面的表达式与另一个表达式进行比较:
>>> def x():
... num = 3 + 4 + 9 * 4
...
>>> x.__code__.co_consts
(None, 3, 4, 9, 7, 36)
看起来字节码编译器采用二进制操作数并存储它们的计算值:
9 * 4 36
3 + 4 7
反汇编函数:
>>> dis.dis(x)
2 0 LOAD_CONST 4 (7)
3 LOAD_CONST 5 (36)
6 BINARY_ADD
7 STORE_FAST 0 (num)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
有趣的是,如果你采用这个表达式:2 + 5 * 8 - 5 + 23 * 4
,co_consts
将是 (None, 2, 5, 8, 23, 4, 40, 92)
只计算乘法:5 * 8
和 23 * 4
加法和减法被忽略。
这个优化是如何真正起作用的?我只在 2.7 上测试过这个。
没有解释这个 ;-) 这意味着它是一个完全反映实现细节的黑暗角落。无论好坏,负责的 "peephole optimizer" 不是在程序的解析树上工作,而是在生成的字节码上工作。这使用起来很笨拙,所以很难预测会发生什么,而且它会随着版本的不同而变化。比如这里下Python 3.6.1:
>>> def f():
... return 2 + 5 * 8 - 5 + 23 * 4
>>> f.__code__.co_consts
(None, 2, 5, 8, 23, 4, 40, 42, 37, 92, 129)
>>> import dis
>>> dis.dis(f)
2 0 LOAD_CONST 10 (129)
2 RETURN_VALUE
所以表达式被折叠成它的最终值,但所有中间值都留在常量元组中。
@COLDSPEED 已经在他们的评论中链接到源代码,关于它的任何问题的唯一真正答案必须来自盯着 CPython 版本中使用的 peephole.c
你 运行。随着时间的推移,它慢慢变得更加雄心勃勃,但背后并没有真正的计划。
这是一道优化题。我在函数中有一个表达式,如下所示:
>>> def x():
... num = 2 * 4 * 100 * 20
...
>>> x.__code__.co_consts
(None, 2, 4, 100, 20, 8, 800, 16000)
表达式2 * 4 * 100 * 20
的结果是16000
,所以如果我们反汇编x
:
>>> dis.dis(x)
2 0 LOAD_CONST 7 (16000)
3 STORE_FAST 0 (x)
6 LOAD_CONST 0 (None)
9 RETURN_VALUE
16000 几乎是所需要的。 co_consts
存储 8 和 800 技术上不再需要我们有总数还是它们?
将上面的表达式与另一个表达式进行比较:
>>> def x():
... num = 3 + 4 + 9 * 4
...
>>> x.__code__.co_consts
(None, 3, 4, 9, 7, 36)
看起来字节码编译器采用二进制操作数并存储它们的计算值:
9 * 4 36
3 + 4 7
反汇编函数:
>>> dis.dis(x)
2 0 LOAD_CONST 4 (7)
3 LOAD_CONST 5 (36)
6 BINARY_ADD
7 STORE_FAST 0 (num)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
有趣的是,如果你采用这个表达式:2 + 5 * 8 - 5 + 23 * 4
,co_consts
将是 (None, 2, 5, 8, 23, 4, 40, 92)
只计算乘法:5 * 8
和 23 * 4
加法和减法被忽略。
这个优化是如何真正起作用的?我只在 2.7 上测试过这个。
没有解释这个 ;-) 这意味着它是一个完全反映实现细节的黑暗角落。无论好坏,负责的 "peephole optimizer" 不是在程序的解析树上工作,而是在生成的字节码上工作。这使用起来很笨拙,所以很难预测会发生什么,而且它会随着版本的不同而变化。比如这里下Python 3.6.1:
>>> def f():
... return 2 + 5 * 8 - 5 + 23 * 4
>>> f.__code__.co_consts
(None, 2, 5, 8, 23, 4, 40, 42, 37, 92, 129)
>>> import dis
>>> dis.dis(f)
2 0 LOAD_CONST 10 (129)
2 RETURN_VALUE
所以表达式被折叠成它的最终值,但所有中间值都留在常量元组中。
@COLDSPEED 已经在他们的评论中链接到源代码,关于它的任何问题的唯一真正答案必须来自盯着 CPython 版本中使用的 peephole.c
你 运行。随着时间的推移,它慢慢变得更加雄心勃勃,但背后并没有真正的计划。