Поиск списка быстрее, чем кортеж?
В прошлом, когда мне нужны индексы с индексом типа в жестком цикле, я обычно использую кортежи, поскольку они, как представляется, в целом чрезвычайно эффективны (близко к использованию только n-числа переменных). Тем не менее, я решил подвергнуть сомнению это предположение сегодня и придумал некоторые неожиданные результаты:
In [102]: l = range(1000)
In [103]: t = tuple(range(1000))
In [107]: timeit(lambda : l[500], number = 10000000)
Out[107]: 2.465047836303711
In [108]: timeit(lambda : t[500], number = 10000000)
Out[108]: 2.8896381855010986
Похоже, что поиск в кортежей занимает 17% дольше, чем просмотры списков! Аналогичные результаты дали повторные эксперименты. Разбор каждого, я нашел их обоих:
In [101]: dis.dis(lambda : l[5])
1 0 LOAD_GLOBAL 0 (l)
3 LOAD_CONST 1 (5)
6 BINARY_SUBSCR
7 RETURN_VALUE
Для справки, типичный поиск/возврат глобальной переменной в 10 000 000 занимает 2.2 секунды. Кроме того, я запустил его без лямбда, на всякий случай (обратите внимание, что число = 100 000 000, а не 10 000 000).
In [126]: timeit('t[500]', 't=range(1000)', number=100000000)
Out[126]: 6.972800970077515
In [127]: timeit('t[500]', 't=tuple(range(1000))', number=100000000)
Out[127]: 9.411366939544678
Здесь поиск кортежей занимает 35% дольше. Что здесь происходит? Для очень плотных циклов это действительно похоже на значительное несоответствие. Что может быть причиной этого?
Обратите внимание, что для декомпозиции в переменную (например, x, y = t) кортежи немного быстрее (~ 6% в моих меньших тестах меньше времени), а для построения из фиксированного числа аргументов кортежи быстрее сумасшедшие (~ 83 % меньше времени). Не принимайте эти результаты как общие правила; Я просто выполнил несколько минут, которые для большинства проектов будут бессмысленными.
In [169]: print(sys.version)
2.7.1 (r271:86882M, Nov 30 2010, 09:39:13)
[GCC 4.0.1 (Apple Inc. build 5494)]
Ответы
Ответ 1
Кортежи в первую очередь быстрее для создания списков, а не для их доступа.
Корреспонденты должны быть немного быстрее для доступа: им требуется меньше косвенности. Однако я считаю, что основное преимущество заключается в том, что при построении списка они не требуют второго выделения.
Списки причин немного быстрее для поиска, потому что у него есть специальная оптимизация для Python:
case BINARY_SUBSCR:
w = POP();
v = TOP();
if (PyList_CheckExact(v) && PyInt_CheckExact(w)) {
/* INLINE: list[int] */
Py_ssize_t i = PyInt_AsSsize_t(w);
if (i < 0)
i += PyList_GET_SIZE(v);
if (i >= 0 && i < PyList_GET_SIZE(v)) {
x = PyList_GET_ITEM(v, i);
Py_INCREF(x);
}
С этой оптимизацией прокомментировано, что кортежи очень немного быстрее, чем списки (примерно на 4%).
Обратите внимание, что добавление отдельной специальной оптимизации для кортежей здесь не является хорошей идеей. Каждый специальный случай, подобный этому в основной части цикла VM, увеличивает размер кода, что уменьшает согласованность кэша, и это означает, что для каждого другого типа поиска требуется дополнительная ветка.
Ответ 2
Вопреки этому, у меня есть совершенно разные советы.
Если данные - по характеру проблемы - фиксированы по длине, используйте кортеж.
Примеры:
- (r, g, b) - три элемента, заданные определением проблемы.
- (широта, долгота) - два элемента, определенные определением проблемы
Если данные - по характеру проблемы - переменная, используйте список.
Скорость не проблема.
Значение должно быть единственным соображением.