Что делает сумма? - PullRequest
       13

Что делает сумма?

0 голосов
/ 01 июня 2018

Сначала я хочу проверить использование памяти между генератором и списком. В книге приведен небольшой фрагмент кода, и я запускаю его на своем компьютере (python3.6, Windows), чтобы найти что-то неожиданное.

  1. В книге сказано, что понимание списков должно создавать реальный список и выделять для него память, поэтому прерывание из списка должно происходить медленнее, чем прерывание из генератора.
  2. Конечно, для понимания списка используется больше памяти, чем для генератора.

FOllowing - мой код, который не удовлетворяет предыдущему мнению (в рамках функции sum).

import tracemalloc
from time import time


def timeIt(func):
    start = time()
    func()
    print('%s use time' % func.__name__, time() - start)
    return func


tracemalloc.start()

numbers = range(1, 1000000)


@timeIt
def lStyle():
    return sum([i for i in numbers if i % 3 == 0])


@timeIt
def gStyle():
    return sum((i for i in numbers if i % 3 == 0))


lStyle()

gStyle()

shouldSize = [i for i in numbers if i % 3 == 0]

snapshotL = tracemalloc.take_snapshot()
top_stats = snapshotL.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)

Вывод:

lStyle use time 0.4460000991821289
gStyle use time 0.6190001964569092
[ Top 10 ]
F:/py3proj/play.py:31: size=11.5 MiB, count=333250, average=36 B
F:/py3proj/play.py:33: size=448 B, count=1, average=448 B
F:/py3proj/play.py:22: size=136 B, count=1, average=136 B
F:/py3proj/play.py:17: size=136 B, count=1, average=136 B
F:/py3proj/play.py:14: size=76 B, count=2, average=38 B
F:/py3proj/play.py:8: size=34 B, count=1, average=34 B

Две точки:

  • Генератор использует больше времени и столько же памяти.
  • Функция понимания списка в сумме, кажется, не создает общий список

Я думаю, что, возможно, функция суммы сделала что-то, чего я не знаю. Кто может это объяснить??

Книга написана с высокой производительностью Python.chapter 5. Но яЯ отличался от книги, чтобы проверить достоверность в другом контексте.И его код здесь book_code , он не помещал списочное понимание в сумму sum.

1 Ответ

0 голосов
/ 01 июня 2018

Когда дело доходит до теста производительности времени, я полагаюсь на модуль 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 для чего-то, для чего оно не предназначено,Он нацелен на поиск возможных утечек памяти (большие блоки памяти никогда не освобождаются), а не на управление памятью, используемой отдельными объектами.


ВНИМАНИЕ : я мог проверять только небольшиеразмеры.Но для очень больших размеров список может исчерпать доступную память, и машина будет использовать виртуальную память из раздела подкачки.В этом случае версия списка станет намного медленнее.Подробнее там

...