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

Почему функция `float` медленнее, чем умножение на 1.0?

Я понимаю, что это можно утверждать как не-проблему, но я пишу программное обеспечение для сред HPC, поэтому это увеличение скорости на 3,5 раза действительно имеет значение.

In [1]: %timeit 10 / float(98765)            
1000000 loops, best of 3: 313 ns per loop

In [2]: %timeit 10 / (98765 * 1.0)
10000000 loops, best of 3: 80.6 ns per loop

Я использовал dis, чтобы посмотреть на код, и я предполагаю, что float() будет медленнее, поскольку для этого требуется вызов функции (к сожалению, я не мог dis.dis(float) видеть, что он на самом деле делает).

Я предполагаю, что второй вопрос будет, когда следует использовать float(n) и когда следует использовать n * 1.0?

4b9b3361

Ответ 1

Поскольку оптимизатор Peep-дыр оптимизирует его, предварительно вычисляя результат этого умножения

import dis
dis.dis(compile("10 / float(98765)", "<string>", "eval"))

  1           0 LOAD_CONST               0 (10)
              3 LOAD_NAME                0 (float)
              6 LOAD_CONST               1 (98765)
              9 CALL_FUNCTION            1
             12 BINARY_DIVIDE       
             13 RETURN_VALUE        

dis.dis(compile("10 / (98765 * 1.0)", "<string>", "eval"))

  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               3 (98765.0)
              6 BINARY_DIVIDE       
              7 RETURN_VALUE        

Сохраняет результат 98765 * 1.0 в байтовом коде как постоянное значение. Таким образом, он просто должен загрузить его и разделить, где, как и в первом случае, мы должны вызвать функцию.

Мы видим, что еще более ясно, как это

print compile("10 / (98765 * 1.0)", "<string>", "eval").co_consts
# (10, 98765, 1.0, 98765.0)

Поскольку значение предварительно вычисляется во время самого компиляции, второе - быстрее.

Изменить: Как указано Davidmh в комментариях,

И причина, по которой это также не оптимизирует деление, состоит в том, что его поведение зависит от флагов, таких как from __future__ import division, а также из-за флага -Q.

Цитата комментарий от фактического кода оптимизатора для Python 2.7.9,

        /* Cannot fold this operation statically since
           the result can depend on the run-time presence
           of the -Qnew flag */