Почему, если 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, а затем проверка...