Я заметил следующее нечетное поведение при выборе времени enumerate
с указанным параметром по умолчанию start
:
In [23]: %timeit enumerate([1, 2, 3, 4])
The slowest run took 7.18 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 511 ns per loop
In [24]: %timeit enumerate([1, 2, 3, 4], start=0)
The slowest run took 12.45 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 1.22 µs per loop
Итак, примерно 2-кратное замедление для случая, когда start
указано.
Байт-код, выданный для каждого случая, на самом деле не указывает ничего, что способствовало бы значительной разнице в скорости. Например, после изучения различных вызовов с помощью dis.dis
дополнительные команды:
18 LOAD_CONST 5 ('start')
21 LOAD_CONST 6 (0)
Эти, наряду с CALL_FUNCTION
с 1 ключевым словом, являются единственными отличиями.
Я пробовал отслеживать вызовы, сделанные в CPython
ceval
с помощью gdb
и оба, похоже, используют do_call
в CALL_FUNCTION
, а не какая-то другая оптимизация, которую я мог обнаружить.
Теперь я понимаю, что enumerate
просто создает итератор перечисления, поэтому мы имеем дело с созданием объекта здесь (правильно?). Я просмотрел Objects/enumobject.c
, пытаясь определить любые различия, если был указан start
. Единственное, что (я полагаю) отличается от того, когда start != NULL
, в котором происходит следующее:
if (start != NULL) {
start = PyNumber_Index(start);
if (start == NULL) {
Py_DECREF(en);
return NULL;
}
assert(PyInt_Check(start) || PyLong_Check(start));
en->en_index = PyInt_AsSsize_t(start);
if (en->en_index == -1 && PyErr_Occurred()) {
PyErr_Clear();
en->en_index = PY_SSIZE_T_MAX;
en->en_longindex = start;
} else {
en->en_longindex = NULL;
Py_DECREF(start);
}
Что не похоже на что-то, что приведет к спаду 2x. (Я думаю, не уверен.)
Предыдущие сегменты кода были выполнены на Python 3.5
, аналогичные результаты присутствуют и в 2.x
.
Вот где я застрял и не могу понять, где искать. Это может быть просто накладными расходами на дополнительные вызовы во втором случае, накапливающимися, но опять же, я не уверен. Кто-нибудь знает, что может быть причиной этого?