Почему этот Genexp выполняет хуже, чем понимание списка?
-
24-09-2019 - |
Вопрос
Я пытался найти самый быстрый способ сосредоточить количество элементов в списке, соответствующий определенному фильтру. В этом случае нахождение того, сколько нечетных чисел в списке.
При этом я был удивлен результатами сравнения понимания списка против эквивалентного выражения генератора:
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 миллионами предметов ...?
(Кстати, самый быстрый способ, которым я нашел, был: x = 1; len(filter(x.__and__, L))
. Отказ И да, я знаю, что написание кода, как это убивает котята, я делаю это ради удовольствия от этого)
Решение
Когда доступна по существу неограниченная память (которая неизбежно будет в крошечных тестах, хотя часто не в реальных проблемах! -), списки будут склонны превзойти генераторы, потому что они могут выделяться только один раз, в одной «большой кучке» ( Никакая фрагментация памяти и т. Д.), В то время как генераторы требуют (внутренне) дополнительные усилия, чтобы избежать этого подхода «Big Bouch», сохраняя состояние кадра стека, чтобы разрешить возобновление выполнения.
Будет ли списком или генератор-подход будет быстрее в реальной программе Зависит от точной ситуации на память, включая фрагментацию, которая о невозможном можно точно воспроизводить в «микро-эталон». В итоге, в итоге, если вы действительно заботитесь о производительности, вы должны тщательно ориентировать (и, отдельно, профиль) вашим фактическим программами (ы), а не просто «игрушкой» микро-ориентиров, в общем случае.
Другие советы
От того, что я помню, для каждого результата необходимо активировать рамку генератора, тогда как понимание списка использует одну раму активации. Инкрементная стоимость в списке сжатие является дополнительной стоимостью памяти - ссылки на 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