Подтвердить что ты не робот

Оптимизация арифметических выражений - что называется этой техникой?

Обсуждение с другом привело к следующей реализации:

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

Второй пример явно более эффективен, просто уменьшив количество инструкций.

Мой вопрос в том, есть ли название для этой оптимизации и почему это не происходит в первом примере?

Кроме того, я не уверен, что это дубликат Почему GCC не оптимизирует a * a * a * a * a * a (a * a * a ) * (a * a * a)?; если это объясняется, пожалуйста, еще немного, поскольку это относится к Python.

4b9b3361

Ответ 1

Эта методика оптимизации называется постоянной сгибанием.

Причина постоянного сгибания, происходящего в последнем коде, но не в первом, заключается в том, что 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__, мы не можем сгинуть константу во втором случае. Рассмотрим этот пример смешного класса, который возвращает подкласс int, который определяет __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) в последнем случае.