Почему, если True медленнее, чем 1?

Почему if True медленнее, чем if 1 в Python? Не должно if True быть быстрее, чем if 1?

Я пытался изучить модуль timeit. Начиная с основ, я пробовал:

>>> def test1():
...     if True:
...         return 1
...     else:
...         return 0

>>> print timeit("test1()", setup = "from __main__ import test1")
0.193144083023


>>> def test2():
...     if 1:
...         return 1
...     else:
...         return 0

>>> print timeit("test2()", setup = "from __main__ import test2")
0.162086009979


>>> def test3():
...     if True:
...             return True
...     else:
...             return False

>>> print timeit("test3()", setup = "from __main__ import test3")
0.214574098587

>>> def test4():
...     if 1:
...             return True
...     else:
...             return False

>>> print timeit("test4()", setup = "from __main__ import test4")
0.160849094391

Я смущен этими вещами:

  • В соответствии с ответом г-на Сильвана Defresne в этот вопрос, все сначала неявно преобразуется в bool, а затем проверяется. Итак, почему if True медленнее, чем if 1?
  • Почему test3 медленнее, чем test1, хотя разные значения return отличаются?
  • Как и вопрос 2, но почему test4 немного быстрее, чем test2?

ПРИМЕЧАНИЕ. Я провел timeit три раза и взял среднее значение результатов, а затем разместил здесь время вместе с кодом.

Этот вопрос не связан с тем, как делать микро-бенчмаркинг (который я сделал в этом примере, но я также понимаю, что он слишком простой), но почему проверка переменной "True" медленнее, чем константа.

Ответ 1

True и False не являются ключевыми словами в Python 2.

Они должны решаться во время выполнения. Это было изменено в Python 3

Те же тесты на Python 3:

>>> timeit.timeit('test1()',setup="from __main__ import test1", number=10000000)
2.806439919999889
>>> timeit.timeit('test2()',setup="from __main__ import test2", number=10000000)
2.801301520000038
>>> timeit.timeit('test3()',setup="from __main__ import test3", number=10000000)
2.7952816800000164
>>> timeit.timeit('test4()',setup="from __main__ import test4", number=10000000)
2.7862537199999906

Ошибка времени в 1%, что приемлемо.

Ответ 2

Разборка по байткоду делает очевидным отличие.

>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (True)
              3 JUMP_IF_FALSE            5 (to 11)
              6 POP_TOP             

  3           7 LOAD_CONST               1 (1)
             10 RETURN_VALUE        
        >>   11 POP_TOP             

  5          12 LOAD_CONST               2 (0)
             15 RETURN_VALUE        
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

Как упоминал Кейби, True и False являются глобалами в Python 2. Многие из них собираются получить к ним доступ.

>>> dis.dis(test2)
  3           0 LOAD_CONST               1 (1)
              3 RETURN_VALUE        

Компилятор Python смог распознать 1 как постоянно "правдивое" выражение и оптимизировать избыточное состояние!

>>> dis.dis(test3)
  2           0 LOAD_GLOBAL              0 (True)
              3 JUMP_IF_FALSE            5 (to 11)
              6 POP_TOP             

  3           7 LOAD_GLOBAL              0 (True)
             10 RETURN_VALUE        
        >>   11 POP_TOP             

  5          12 LOAD_GLOBAL              1 (False)
             15 RETURN_VALUE        
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

Совсем аналогично test1, с еще одним LOAD_GLOBAL.

>>> dis.dis(test4)
  3           0 LOAD_GLOBAL              0 (True)
              3 RETURN_VALUE        

См. test2. Но LOAD_GLOBAL является более дорогостоящим, чем LOAD_CONST.

Ответ 3

Моя догадка заключается в том, что в конечном итоге ВСЕ (включая булевы) нужно преобразовать в 0 и 1, двоичные файлы, которые распознают машины. Таким образом, проверка того, что 1 = 1 быстрее, чем преобразование True в 1, а затем проверка...