( Обновление: Может происходить только в CPython 3.8 32-битных для Windows, поэтому не удивляйтесь, если вы не сможете воспроизвести его в других версиях. См. Таблицы в разделе Обновление.)
И iter
, и reversed
приводят к специализированным итераторам для списков:
>>> iter([1, 2, 3])
<list_iterator object at 0x031495C8>
>>> reversed([1, 2, 3])
<list_reverseiterator object at 0x03168310>
Но reversed
намного медленнее:
> python -m timeit -s "a = list(range(1000))" "list(iter(a))"
50000 loops, best of 5: 5.76 usec per loop
> python -m timeit -s "a = list(range(1000))" "list(reversed(a))"
20000 loops, best of 5: 14.2 usec per loop
И я могу это последовательно воспроизвести. Позже я попробовал iter
еще пять раз, взял 5,98, 5,84, 5,85, 5,87, 5,86. Затем reversed
еще пять раз - 14,3, 14,4, 14,4, 14,5, 14,3.
Я подумал, что, возможно, iter
выиграет от увеличения мест в памяти элементов списка, поэтому я попытался перевернуть список заранее. То же изображение:
> python -m timeit -s "a = list(range(1000)); a.reverse()" "list(iter(a))"
50000 loops, best of 5: 5.73 usec per loop
> python -m timeit -s "a = list(range(1000)); a.reverse()" "list(reversed(a))"
20000 loops, best of 5: 14.1 usec per loop
То же самое с sum
:
> python -m timeit -s "a = list(range(1000))" "sum(iter(a))"
20000 loops, best of 5: 10.7 usec per loop
> python -m timeit -s "a = list(range(1000))" "sum(reversed(a))"
10000 loops, best of 5: 20.9 usec per loop
И с одинаковыми элементами:
> python -m timeit -s "a = [None] * 1000" "list(iter(a))"
50000 loops, best of 5: 6.35 usec per loop
> python -m timeit -s "a = [None] * 1000" "list(reversed(a))"
20000 loops, best of 5: 14.5 usec per loop
Почему обратный итератор намного медленнее?
Я использую CPython 3.8.1 32 бит на Windows 10 pro 64 бит версия 1903 с Intel i5-7200U (это HUAWEI MateBook X). Никакой специальной конфигурации, просто обычная Python установка при обычной Windows установке.
Обновление: Я провел более крупный автоматический тест с восемью различными Python версиями (все недавно установлены с настройками по умолчанию) на другом компьютере (Pentium N3700, Windows 10 Pro 64-bit, 1903). Используемое время c на л oop:
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 16.6 17.0 15.2 16.2
3.6.8 16.8 17.2 14.9 15.8
3.7.6 16.5 16.9 14.8 15.5
3.8.1 16.3 22.1 14.6 15.5
Обратите внимание: две вещи:
- Python 3.8.1 32-битная
reversed
является единственной один намного медленнее. Можно объяснить, почему почти никто другой не мог воспроизвести его. - Во всех семи других версиях
reversed
был немного медленнее, чем iter
. Приблизительно 0,4 используют c в 32-разрядных и около 0,9 используют c в 64-разрядных.
Я выполнил эти 16 тестов в циклическом режиме в течение десяти раундов, и каждый раз, как показано выше является лучшим из его десяти источников времени. Каждое из 160 исходных значений времени было выполнено так:
python.exe -m timeit -r 5 -s "a = list(range(1000))" "list(iter(a))"
or
python.exe -m timeit -r 5 -s "a = list(range(1000))" "list(reversed(a))"
Время для каждого из 16 тестов было достаточно постоянным. Полная таблица (обратите внимание, что циклический перебор означает, что я запускал эти столбцы по столбцам, а не по строкам):
3.5.4 32-bit iter [16.7, 16.6, 17.3, 16.6, 16.7, 16.6, 16.6, 16.6, 16.6, 16.7]
3.5.4 32-bit reversed [17.1, 17.1, 17.1, 17.2, 17.1, 17.1, 17.0, 17.1, 17.1, 17.1]
3.5.4 64-bit iter [15.2, 15.4, 15.4, 15.4, 15.4, 15.4, 15.4, 15.3, 15.4, 15.3]
3.5.4 64-bit reversed [16.8, 16.2, 16.3, 16.3, 16.2, 16.2, 16.2, 16.2, 16.2, 16.3]
3.6.8 32-bit iter [17.3, 16.9, 16.8, 16.9, 16.9, 16.8, 16.9, 16.9, 16.8, 16.8]
3.6.8 32-bit reversed [17.2, 17.2, 17.2, 17.3, 17.3, 17.3, 17.3, 17.2, 17.2, 17.2]
3.6.8 64-bit iter [15.0, 14.9, 15.9, 14.9, 14.9, 15.0, 14.9, 14.9, 14.9, 14.9]
3.6.8 64-bit reversed [15.8, 15.9, 16.4, 15.9, 15.9, 16.0, 15.8, 15.9, 15.9, 15.8]
3.7.6 32-bit iter [16.6, 17.2, 16.6, 16.5, 16.7, 16.7, 16.5, 16.5, 16.5, 16.7]
3.7.6 32-bit reversed [17.2, 17.6, 17.0, 17.0, 16.9, 17.2, 17.3, 17.0, 17.5, 17.0]
3.7.6 64-bit iter [14.8, 15.1, 14.9, 14.9, 14.8, 15.1, 14.9, 14.8, 15.0, 14.9]
3.7.6 64-bit reversed [16.0, 20.1, 15.7, 15.6, 15.6, 15.6, 15.7, 15.7, 15.8, 15.5]
3.8.1 32-bit iter [16.4, 16.6, 16.3, 16.4, 16.5, 16.4, 16.5, 16.4, 16.8, 16.4]
3.8.1 32-bit reversed [22.3, 22.4, 22.2, 22.3, 22.3, 22.3, 22.5, 22.4, 22.3, 22.1]
3.8.1 64-bit iter [14.6, 15.1, 14.6, 14.7, 14.7, 14.7, 14.7, 14.6, 14.6, 14.6]
3.8.1 64-bit reversed [15.5, 16.1, 15.5, 15.6, 15.5, 15.5, 15.5, 15.5, 15.5, 15.5]
Тот же тест в списке с миллионом значений (list(range(250)) * 4000
). Времена mse c на л oop:
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 19.8 19.9 22.4 22.7
3.6.8 19.8 19.9 22.3 22.6
3.7.6 19.9 19.9 22.3 22.5
3.8.1 19.8 24.9 22.4 22.6
Разница еще меньше, за исключением того, что reversed
на 3.8.1 32-битная снова намного медленнее.
One больше, просто с CPython 3.8.0 вместо 3.8.1, где это также происходит.
32-bit 64-bit
CPython iter reversed iter reversed
3.5.4 19.5 19.6 21.9 22.2
3.6.8 19.5 19.7 21.8 22.1
3.7.6 19.5 19.6 21.7 22.0
3.8.0 19.4 24.5 21.7 22.1