Почему этот genexp работает хуже, чем понимание списка?

Я пытался найти самый быстрый способ считать количество объектов в списке, соответствующем определенному фильтру. В этом случае, находя, сколько нечетных чисел там находится в списке.

При выполнении этого я был удивлен результатами сравнения понимания списка по сравнению с эквивалентным выражением генератора:

python -m timeit -s "L = xrange(1000000)" "sum([1 for i in L if i & 1])"
10 loops, best of 3: 109 msec per loop

python -m timeit -s "L = xrange(1000000)" "sum(1 for i in L if i & 1)"
10 loops, best of 3: 125 msec per loop

Я также попробовал L быть обычным списком и различными размерами, но во всех случаях победы понимания списка.

Что genexp делает, который заставляет его быть медленнее по сравнению с listcomp, который создает новый список с 1 миллионом объектов...?

(Btw, самый быстрый способ, которым я нашел, был: x = 1; len(filter(x.__and__, L)). И да, я знаю, что написание кода как этот уничтожает котят, я делаю его ради удовольствия),

7
задан truppo 31 January 2010 в 23:23
поделиться

2 ответа

Когда доступна по существу неограниченная память (что неизменно будет происходить в крошечных бенчмарках, хотя часто и не в реальных проблемах!-), списки будут иметь тенденцию опережать генераторы, потому что они могут быть распределены только один раз, в одной "большой связке" (без фрагментации памяти и т.д.), в то время как генераторы требуют (внутренне) дополнительных усилий, чтобы избежать этого "большого связки" путем сохранения состояния стек-фрейма, чтобы позволить возобновить исполнение.

Будет ли законченный подход или генератор-подход быстрее в реальной программе зависит от точной ситуации с памятью, в том числе от фрагментации, которую почти невозможно точно воспроизвести в "микро-бенчмарке". IOW, в конце концов, если вы действительно заботитесь о производительности, вы должны тщательно сравнивать (и, отдельно, профиль) вашу реальную программу (программы), а не только "игрушечные" микробенчмарки, в общем случае.

15
ответ дан 6 December 2019 в 10:50
поделиться

Насколько я помню, кадр генератора должен быть активирован для каждого результата, тогда как понимание списка использует один кадр активации. Дополнительные затраты при сжатии списка - это добавленная стоимость памяти - в вашем случае ссылки на int. Отношение может полностью измениться, если каждый элемент является новым экземпляром и использует больше памяти. Обновление

: после тестирования оно полностью изменило

~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum([oint(1) for i in L if i & 1])" 
10 loops, best of 3: 414 msec per loop

~% python -m timeit -s "L = xrange(1000000);oint=type('intEx', (int,),{})" "sum(oint(1) for i in L if i & 1)" 
10 loops, best of 3: 392 msec per loop
3
ответ дан 6 December 2019 в 10:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: