Я поместил эти фрагменты в две функции:
def first():
a=100000000
while a > 0:
a-=10
def second():
a=100000000
while True:
a-=10
if a <= 0:
break
И затем использовал timeit.timeit
, и на самом деле это был second
, который был последовательно рассчитан как более продолжительный (хотя для действительно незначительной разницы).
from timeit import timeit
timeit(first, number=100)
timeit(second, number=100)
Затем я проверил байт-код, скопировав пример из dis docs . Это выявило, что байт-код почти одинаков для обоих фрагментов, единственное отличие состоит в том, как упорядочены коды операций, и что second
включает в себя одну дополнительную инструкцию, а именно BREAK_LOOP
.
import dis
from collections import Counter
first_bytecode = dis.Bytecode(first)
for instr in first_bytecode:
print(instr.opname)
second_bytecode = dis.Bytecode(second)
for instr in second_bytecode:
print(instr.opname)
# easier to compare with Counters
first_b = Counter(x.opname for x in first_bytecode)
sec_b = Counter(x.opname for x in second_bytecode)
Наконец,Можно подумать, что порядок может иметь значение (и может), но для правильного сравнения second
должен сначала проверить условие разрыва, прежде чем вычесть из a
. Затем рассмотрим третью функцию:
def third():
a=100000000
while True:
if a <= 0:
break
a-=10
Сравнивая байт-код third
, вы увидите, что он фактически упорядочен так же, как и байт-код first
, единственное отличие - один BREAK_LOOP
заклинило где-то посередине.
Если что-нибудь, я надеюсь, это покажет вам, как незначительное выполнение одного кода операции обычно по отношению к общей производительности кода. Я думаю бессмертные слова Дональда Кнута особенно хорошо подходят для этого случая:
Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - этокорень зла.