Прежде всего, обратите внимание, что мы не видим то же самое в Python 2.x:
>>> timeit("for i in range(1000): 2*i*i")
51.00784397125244
>>> timeit("for i in range(1000): 2*(i*i)")
50.48330092430115
Так что это наводит нас на мысль, что это связано с тем, как изменились целые числа в Python 3:в частности, Python 3 везде использует long
(произвольно большие целые числа).
Для достаточно маленьких целых чисел (включая те, которые мы здесь рассматриваем), CPython фактически просто использует O (MN) grade-алгоритм умножения цифр на цифру (для больших целых чисел он переключается на алгоритм Карацубы ).Вы можете сами увидеть это в источнике .
Количество цифр в x*x
примерно в два раза больше, чем 2*x
или x
(так как log (x 2)) = 2 log (x)).Обратите внимание, что «цифра» в этом контексте - это не цифра от 10 до 30, а 30-битное значение (которые рассматриваются как однозначные в реализации CPython).Следовательно, 2
является однозначным значением, а x
и 2*x
являются однозначными значениями для всех итераций цикла, но x*x
является двузначным для x >= 2**15
.Следовательно, для x >= 2**15
, 2*x*x
требует только умножения на одну цифру, тогда как 2*(x*x)
требует умножения на одну цифру и одну цифру (так как x*x
имеет 2 30-битовые цифры).
Вот прямой способ увидеть это (Python 3):
>>> timeit("a*b", "a,b = 2, 123456**2", number=100000000)
5.796971936999967
>>> timeit("a*b", "a,b = 2*123456, 123456", number=100000000)
4.3559221399999615
Опять же, сравните это с Python 2, который везде не использует целые числа произвольной длины:
>>> timeit("a*b", "a,b = 2, 123456**2", number=100000000)
3.0912468433380127
>>> timeit("a*b", "a,b = 2*123456, 123456", number=100000000)
3.1120400428771973
(Одно интересное примечание: если вы посмотрите на источник, вы увидите, что алгоритм фактически имеет особый случай для возведения в квадрат чисел (что мы здесь делаем), но даже все же этонедостаточно, чтобы преодолеть тот факт, что 2*(x*x)
просто требует обработки большего количества цифр.)