Когда дело доходит до теста производительности времени, я полагаюсь на модуль timeit
, потому что он автоматически выполняет несколько прогонов кода.
В моей системе timeit дает следующие результаты (я сильно уменьшил размеры, потому чтоиз многочисленных прогонов):
>>> timeit.timeit("sum([i for i in numbers if i % 3 == 0])", "numbers = range(1, 1000)")
59.54427594248068
>>> timeit.timeit("sum((i for i in numbers if i % 3 == 0))", "numbers = range(1, 1000)")
64.36398425334801
Таким образом, генератор работает медленнее примерно на 8% (*).Это на самом деле не удивительно, потому что генератор должен выполнить некоторый код на лету , чтобы получить следующее значение, в то время как предварительно вычисленный список только увеличивает свой текущий указатель.
Оценка памяти ИМХО более сложна, поэтому я использовал Вычислить след памяти объекта и его содержимого (рецепт Python) из activestate
>>> numbers = range(1, 100)
>>> numbers = range(1, 100000)
>>> l = [i for i in numbers if i % 3 == 0]
>>> g = (i for i in numbers if i % 3 == 0)
>>> total_size(l)
1218708
>>> total_size(g)
88
>>> total_size(numbers)
48
Моя интерпретация заключается в том, что список использует память для всех своих элементов (чтоне является сюрпризом), в то время как генератору нужно всего несколько значений и некоторый код, поэтому объем памяти для генератора значительно меньше.
Я настоятельно считаю, что вы использовали tracemalloc
для чего-то, для чего оно не предназначено,Он нацелен на поиск возможных утечек памяти (большие блоки памяти никогда не освобождаются), а не на управление памятью, используемой отдельными объектами.
ВНИМАНИЕ : я мог проверять только небольшиеразмеры.Но для очень больших размеров список может исчерпать доступную память, и машина будет использовать виртуальную память из раздела подкачки.В этом случае версия списка станет намного медленнее.Подробнее там