python: while True против while (условие) эффективности - PullRequest
2 голосов
/ 03 ноября 2019

Я недавно начал изучать программирование (на Python). У меня есть два фрагмента кода, которые используют циклы while:

a=100000000
#piece of code 1    
while a > 0:
    a-=10
print("done")

#piece of code 2
while True:
    a-=10
    if a <= 0:
       print("done")
       break

Оба функционально эквивалентны, то есть по сути выполняют одну и ту же задачу. Для любопытства я записал время, необходимое для выполнения этой операции, используя обе версии этого цикла while, используя модуль времени. Результаты были:

фрагмент кода 1: 0,99 с
фрагмент кода 2: 0,89 с

Они показывают, по существу, то же самоепроизводительность, хотя кусок кода 2 был немного более эффективным. Это нормально, потому что эта разница в принципе не имеет значения даже для очень больших чисел. Тем не менее, это неожиданно для меня, так как я считаю, что первый цикл while выполняет меньше операций. Может кто-нибудь объяснить, почему второй фрагмент кода более эффективен?

1 Ответ

2 голосов
/ 04 ноября 2019

Я поместил эти фрагменты в две функции:

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% случаев: преждевременная оптимизация - этокорень зла.

...