算术表达式的优化 - 这种技术被称为什么?

时间:2016-05-12 06:45:28

标签: python optimization arithmetic-expressions

与朋友的讨论导致了以下的实现:

>>> 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。

1 个答案:

答案 0 :(得分:17)

此优化技术称为constant folding

在后一个代码中发生常量折叠而不是前者的原因是Python具有动态类型,而在数学中,实数的乘积是可交换并且自由关联< / em>,在一般情况下在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__的行为,因此在后一种情况下我们无法折叠常量。考虑一个有趣类的示例,该类返回int的子类,将__mul__ / __rmul__定义为返回操作数而不是product的总和:

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)是错误的。