算术表达式的优化 - 这种技术叫什么?
Optimization of arithmetic expressions - what is this technique called?
与朋友的讨论导致了以下认识:
>>> import dis
>>> i = lambda n: n*24*60*60
>>> dis.dis(i)
1 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (24)
6 BINARY_MULTIPLY
7 LOAD_CONST 2 (60)
10 BINARY_MULTIPLY
11 LOAD_CONST 2 (60)
14 BINARY_MULTIPLY
15 RETURN_VALUE
>>> k = lambda n: 24*60*60*n
>>> dis.dis(k)
1 0 LOAD_CONST 4 (86400)
3 LOAD_FAST 0 (n)
6 BINARY_MULTIPLY
7 RETURN_VALUE
第二个例子显然通过减少指令数量更有效。
我的问题是,这个优化是否有名称,为什么第一个示例中没有出现?
此外,我不确定这是否是 Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)? 的副本;如果是,请进一步解释,因为它适用于 Python.
这种优化技术称为constant folding。
常量折叠发生在后一个代码中而不是前一个代码中的原因是 Python 具有动态类型,而在数学中实数的乘积是 可交换的和自由关联,在一般情况下Python不是这样,因为既不是所有变量都包含实数,也不能事先知道类型。
Python 中的乘法是 left-associative - 24 * 60 * 60 * n
表现得像 (((24 * 60) * 60) * n)
,后者又隐式执行像
(24).__mul__(60).__mul__(60).__mul__(n)
或
(n).__rmul_((24).__mul__(60).__mul__(60))
而 n * 24 * 60 * 60
即 (((n * 24) * 60) * 60)
可以 表现得像
n.__mul__(24).__mul__(60).__mul__(60)
或
(24).__rmul__(n).__mul__(60).__mul__(60)
由于我们无法事先知道 n.__mul__
的行为,因此在后一种情况下我们无法折叠常量。考虑这个有趣的 class 示例,returns 是 int
的子 class,它定义 __mul__
/__rmul__
为返回操作数的总和而不是产品:
class MultiplyAsAdd(int):
def __mul__(self, other):
return MultiplyAsAdd(self + other)
def __rmul__(self, other):
return MultiplyAsAdd(other + self)
然后
>>> (lambda n: 24*60*60*n)(MultiplyAsAdd(5))
86405
>>> (lambda n: n*24*60*60)(MultiplyAsAdd(5))
149
显然,在后一种情况下,Python 将产品括在括号中 n*(24*60*60)
是错误的。
与朋友的讨论导致了以下认识:
>>> import dis
>>> i = lambda n: n*24*60*60
>>> dis.dis(i)
1 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (24)
6 BINARY_MULTIPLY
7 LOAD_CONST 2 (60)
10 BINARY_MULTIPLY
11 LOAD_CONST 2 (60)
14 BINARY_MULTIPLY
15 RETURN_VALUE
>>> k = lambda n: 24*60*60*n
>>> dis.dis(k)
1 0 LOAD_CONST 4 (86400)
3 LOAD_FAST 0 (n)
6 BINARY_MULTIPLY
7 RETURN_VALUE
第二个例子显然通过减少指令数量更有效。
我的问题是,这个优化是否有名称,为什么第一个示例中没有出现?
此外,我不确定这是否是 Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)? 的副本;如果是,请进一步解释,因为它适用于 Python.
这种优化技术称为constant folding。
常量折叠发生在后一个代码中而不是前一个代码中的原因是 Python 具有动态类型,而在数学中实数的乘积是 可交换的和自由关联,在一般情况下Python不是这样,因为既不是所有变量都包含实数,也不能事先知道类型。
Python 中的乘法是 left-associative - 24 * 60 * 60 * n
表现得像 (((24 * 60) * 60) * n)
,后者又隐式执行像
(24).__mul__(60).__mul__(60).__mul__(n)
或
(n).__rmul_((24).__mul__(60).__mul__(60))
而 n * 24 * 60 * 60
即 (((n * 24) * 60) * 60)
可以 表现得像
n.__mul__(24).__mul__(60).__mul__(60)
或
(24).__rmul__(n).__mul__(60).__mul__(60)
由于我们无法事先知道 n.__mul__
的行为,因此在后一种情况下我们无法折叠常量。考虑这个有趣的 class 示例,returns 是 int
的子 class,它定义 __mul__
/__rmul__
为返回操作数的总和而不是产品:
class MultiplyAsAdd(int):
def __mul__(self, other):
return MultiplyAsAdd(self + other)
def __rmul__(self, other):
return MultiplyAsAdd(other + self)
然后
>>> (lambda n: 24*60*60*n)(MultiplyAsAdd(5))
86405
>>> (lambda n: n*24*60*60)(MultiplyAsAdd(5))
149
显然,在后一种情况下,Python 将产品括在括号中 n*(24*60*60)
是错误的。