Подтвердить что ты не робот

Эффективность индексации списков (python 2 vs python 3)

Отвечая на другой вопрос , я предложил использовать timeit для проверки разницы между индексацией списка с положительными целыми числами или целыми отрицательными целями. Здесь код:

import timeit
t=timeit.timeit('mylist[99]',setup='mylist=list(range(100))',number=10000000)
print (t)
t=timeit.timeit('mylist[-1]',setup='mylist=list(range(100))',number=10000000)
print (t)

Я запустил этот код с помощью python 2.6:

$ python2.6 test.py
0.587687015533
0.586369991302

Затем я запустил его с помощью python 3.2:

$ python3.2 test.py
0.9212150573730469
1.0225799083709717

Затем я почесал голову, сделал небольшой поиск в Google и решил опубликовать эти наблюдения здесь.

Операционная система: OS-X (10.5.8) - Intel Core2Duo

Это кажется довольно значительной разницей для меня (фактор более чем 1,5 разницы). У кого-нибудь есть идея, почему python3 намного медленнее - особенно для такой общей операции?

ИЗМЕНИТЬ

Я запустил тот же код на своем рабочем столе Ubuntu Linux (Intel i7) и добился сопоставимых результатов с python2.6 и python 3.2. Похоже, что это проблема, которая зависит от операционной системы (или процессора) (другие пользователи видят то же поведение на машинах Linux - см. Комментарии).

РЕДАКТИРОВАТЬ 2

В одном из ответов был запрошен баннер запуска, так что:

Python 2.6.4 (r264:75821M, Oct 27 2009, 19:48:32) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin

и

Python 3.2 (r32:88452, Feb 20 2011, 10:19:59) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin

UPDATE

Я только что установил новые версии python2.7.3 и python3.2.3 из http://www.python.org/download/

В обоих случаях я взял

"Python x.x.3 32-разрядный i386/PPC-инсталлятор Mac OS X (для Mac OS X 10.3 по 10.6 [2])"

так как я на OS X 10.5. Вот новые тайминги (которые разумно согласованы с помощью нескольких испытаний):

python 2.7

$python2.7 test.py
0.577006101608
0.590042829514

python 3.2.3

$python3.2 test.py
0.8882801532745361
1.034242868423462
4b9b3361

Ответ 1

Это кажется артефактом некоторых построений Python 3.2. Лучшая гипотеза на этом этапе заключается в том, что все 32-битные сборки Intel имеют замедление, но не 64-битные. Читайте дальше, чтобы узнать подробности.

У вас не было достаточного количества тестов для определения чего-либо. Повторяя ваш тест несколько раз, я получил значения от 0,31 до 0,54 для одного и того же теста, что является огромным изменением.

Итак, я проверил ваш тест с 10x номером и repeat=10, используя кучу разных установок Python2 и Python3. Отбрасывая верхний и нижний результаты, усредняя остальные 8 и делясь на 10 (чтобы получить число, эквивалентное вашим тестам), вот что я увидел:

 1. 0.52/0.53 Lion 2.6
 2. 0.49/0.50 Lion 2.7
 3. 0.48/0.48 MacPorts 2.7
 4. 0.39/0.49 MacPorts 3.2
 5. 0.39/0.48 HomeBrew 3.2

Итак, похоже, что 3.2 на самом деле немного быстрее с [99] и примерно с той же скоростью с [-1].

Однако на машине 10.5 я получил следующие результаты:

 1. 0.98/1.02 MacPorts 2.6
 2. 1.47/1.59 MacPorts 3.2

Вернувшись на исходную машину (Lion), я побежал в 32-битном режиме и получил следующее:

 1. 0.50/0.48 Homebrew 2.7
 2. 0.75/0.82 Homebrew 3.2

Итак, похоже, что важна 32-битность, а не Leopard vs. Lion, gcc 4.0 против gcc 4.2 или clang, аппаратные отличия и т.д. Это помогло бы протестировать 64-битные сборки под Leopard, с разными компиляторы и т.д., но, к сожалению, ящик Leopard - это первый Intel Mini Mini (с 32-разрядным процессором Core Solo), поэтому я не могу выполнить этот тест.

В качестве дополнительных косвенных доказательств я провел целый ряд других быстрых тестов на коробке Lion, и похоже, что 32-битный 3.2 на ~ 50% медленнее, чем 2.x, в то время как 64-битный 3.2 может быть немного быстрее чем 2.x. Но если мы действительно хотим поддержать это, кто-то должен выбрать и запустить настоящий набор тестов.

В любом случае, моя лучшая догадка заключается в том, что при оптимизации ветки 3.x никто не прикладывал больших усилий в 32-разрядные сборки i386 Mac. Это действительно разумный выбор для них.

Или, наоборот, они даже не приложили много усилий в 32-разрядный период i386. Эта возможность может объяснить, почему OP увидел 2.x и 3.2, дающие аналогичные результаты в окне linux, в то время как Otto Allmendinger видел, что 3,2 медленнее, чем 2.6, в линейке Linux. Но так как ни один из них не упомянул, работают ли они на 32-битном или 64-битном Linux, трудно понять, имеет ли это значение.

Есть еще много других возможностей, которые мы не исключаем, но это кажется лучшим.

Ответ 2

Вот код, который иллюстрирует хотя бы часть ответа:

$ python
Python 2.7.3 (default, Apr 20 2012, 22:44:07) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> t=timeit.timeit('mylist[99]',setup='mylist=list(range(100))',number=50000000)
>>> print (t)
2.55517697334
>>> t=timeit.timeit('mylist[99L]',setup='mylist=list(range(100))',number=50000000)
>>> print (t)
3.89904499054

$ python3
Python 3.2.3 (default, May  3 2012, 15:54:42) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import timeit
>>> t=timeit.timeit('mylist[99]',setup='mylist=list(range(100))',number=50000000)
>>> print (t)
3.9906489849090576

python3 не имеет старого типа int.

Ответ 3

Python 3 range() - это Python 2 xrange(). Если вы хотите имитировать код Python 2 range() в коде Python 3, вы должны использовать list(range(num). Чем больше num, тем больше разница будет с вашим исходным кодом.

Индексирование должно быть независимым от того, что хранится внутри списка, поскольку список хранит только ссылки на целевые объекты. Ссылки являются нетипизированными и одинаковыми. Таким образом, тип списка является однородной структурой данных - технически. Индексирование означает превратить значение индекса в начальный адрес + смещение. Вычисление смещения очень эффективно при максимальном вычитании. Это очень дешевая дополнительная операция по сравнению с другими операциями.