Ответ 1
Python предварительно вычисляет вычисления при компиляции в виде так называемой оптимизации peep-hole:
>>> import dis
>>> def version2():
... return ((200*200 - 2) & ((1 << 500000000) - 1)) + ((200*200 - 2) >> 500000000)
...
>>> dis.dis(version2)
2 0 LOAD_CONST 13 (39998)
2 RETURN_VALUE
version2()
возвращает уже вычисленное значение и не работает. Возвращение константы, конечно, намного, намного быстрее, чем вычислять значение каждый раз.
Смотрите fold_binops_on_constants
function в исходном файле peephole.c
Python, чтобы узнать, как это делает компилятор.
В результате компиляция version2
занимает (много) больше времени, чем version1
:
>>> import timeit
>>> version1_text = '''\
... def version1(n, p):
... return ((n*n - 2) & ((1 << p) - 1)) + ((n*n - 2) >> p)
... '''
>>> version2_text = '''\
... def version2():
... return ((200*200 - 2) & ((1 << 500000000) - 1)) + ((200*200 - 2) >> 500000000)
... '''
>>> timeit.timeit("compile(t, '', 'exec')", 'from __main__ import version1_text as t', number=10)
0.00028649598243646324
>>> timeit.timeit("compile(t, '', 'exec')", 'from __main__ import version2_text as t', number=10)
2.2103765579813626
Хорошо, что Python кэширует результаты байткода компиляции!
Промежуточные результаты каждого подвыражения также сохраняются в атрибуте co_consts
объекта кода, а некоторые из них довольно большие:
>>> import sys
>>> consts = version2.__code__.co_consts
>>> for obj in consts:
... size = sys.getsizeof(obj)
... print(f'{type(obj)!s:<18} {size:<8} {"<too large to print>" if size > 100 else obj}')
...
<class 'NoneType'> 16 None
<class 'int'> 28 200
<class 'int'> 28 2
<class 'int'> 28 1
<class 'int'> 28 500000000
<class 'int'> 28 40000
<class 'int'> 28 39998
<class 'int'> 66666692 <too large to print>
<class 'int'> 66666692 <too large to print>
<class 'int'> 28 39998
<class 'int'> 28 40000
<class 'int'> 28 39998
<class 'int'> 24 0
<class 'int'> 28 39998
поэтому это сделало кеш байт-кода немного больше:
>>> import marshal
>>> len(marshal.dumps(version1.__code__))
129
>>> len(marshal.dumps(version2.__code__))
133333481
Это минимум 127 МБ для файла .pyc
для модуля, который содержит вашу версию без аргументов!