Это дополнительный вопрос к ответу, который я дал несколько дней назад . Изменить: кажется, что ОП этого вопроса уже использовал код, который я отправил ему, чтобы задать тот же вопрос , но я не знал об этом. Извиняюсь. Ответы предоставлены разные, хотя!
В основном я заметил, что:
>>> def without_else(param=False):
... if param:
... return 1
... return 0
>>> def with_else(param=False):
... if param:
... return 1
... else:
... return 0
>>> from timeit import Timer as T
>>> T(lambda : without_else()).repeat()
[0.3011460304260254, 0.2866089344024658, 0.2871549129486084]
>>> T(lambda : with_else()).repeat()
[0.27536892890930176, 0.2693932056427002, 0.27011704444885254]
>>> T(lambda : without_else(True)).repeat()
[0.3383951187133789, 0.32756996154785156, 0.3279120922088623]
>>> T(lambda : with_else(True)).repeat()
[0.3305950164794922, 0.32186388969421387, 0.3209099769592285]
... или другими словами: наличие else
предложения быстрее независимо от if
того, вызывается условие или нет.
Я предполагаю, что это связано с различным байт-кодом, сгенерированным этими двумя, но кто-нибудь может подтвердить / объяснить подробно?
РЕДАКТИРОВАТЬ: Кажется, не все могут воспроизвести мои сроки, поэтому я подумал, что было бы полезно дать некоторую информацию о моей системе. Я использую Ubuntu 11.10 64 bit с установленным Python по умолчанию. python
генерирует следующую информацию о версии:
Python 2.7.2+ (default, Oct 4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Вот результаты разборки в Python 2.7:
>>> dis.dis(without_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
4 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
>>> dis.dis(with_else)
2 0 LOAD_FAST 0 (param)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 (1)
9 RETURN_VALUE
5 >> 10 LOAD_CONST 2 (0)
13 RETURN_VALUE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
источник
LOAD_CONST(None); RETURN_VALUE
но, как уже говорилось, он никогда не достигается) в концеwith_else
. Я очень сомневаюсь, что мертвый код делает функцию быстрее. Может ли кто-нибудь предоставитьdis
на 2.7?else
иFalse
была самой медленной из всех (152 нс). Второй самый быстрый былTrue
безelse
(143 нс), а два других были в основном одинаковыми (137 нс и 138 нс). Я не использовал параметр по умолчанию и измерял его%timeit
в iPython.with_else
это заметно быстрее.Ответы:
Это чистое предположение, и я не нашел простой способ проверить, правильно ли это, но у меня есть для вас теория.
Я пробовал ваш код и получал те же результаты,
without_else()
что несколько раз медленнее, чемwith_else()
:Учитывая, что байт-код идентичен, единственным отличием является имя функции. В частности, проверка синхронизации выполняет поиск глобального имени. Попробуйте переименовать,
without_else()
и разница исчезнет:Я предполагаю, что у
without_else
него есть коллизия хешей с чем-то другим,globals()
поэтому поиск по глобальному имени немного медленнее.Редактировать : словарь с 7 или 8 ключами, вероятно, имеет 32 слота, поэтому на этом основании
without_else
имеет коллизию хеша с__builtins__
:Чтобы уточнить, как работает хеширование:
__builtins__
хеширование до -1196389688, которое уменьшило по модулю размер таблицы (32), означает, что оно хранится в слоте # 8 таблицы.without_else
хеширует до 505688136, что по модулю 32 уменьшено до 8, поэтому происходит коллизия. Чтобы решить этот Python рассчитывает:Начиная с:
Повторяйте это, пока мы не найдем свободный слот:
что дает 17 для использования в качестве следующего индекса. К счастью, это бесплатно, поэтому цикл повторяется только один раз. Размер хеш-таблицы равен степени 2, так же
2**i
как и размер хеш-таблицы,i
это количество битов, используемых из хеш-значенияj
.Каждый зонд в таблице может найти один из них:
Слот пуст, в этом случае зондирование прекращается, и мы знаем, что значение отсутствует в таблице.
Слот не используется, но использовался в прошлом, и в этом случае мы пробуем следующее значение, рассчитанное, как указано выше.
Слот заполнен, но полное значение хеша, хранящееся в таблице, не совпадает с хешем ключа, который мы ищем (это то, что происходит в случае
__builtins__
vswithout_else
).Слот заполнен и имеет именно то значение хеша, которое нам нужно, затем Python проверяет, являются ли ключ и объект, который мы ищем, одним и тем же объектом (который в этом случае будет, потому что короткие строки, которые могут быть идентификаторами, интернированы так, идентичные идентификаторы используют одну и ту же строку).
Наконец, когда слот заполнен, хеш точно совпадает, но ключи не являются идентичным объектом, тогда и только тогда Python попытается сравнить их на равенство. Это сравнительно медленно, но в случае поиска имен на самом деле не должно происходить.
источник