Расхождения в производительности между itertools.product и списком - PullRequest
0 голосов
/ 26 апреля 2020

Я изучал модуль Python itertools и наткнулся на функцию itertools.product, которая возвращает то же самое, что и ((x,y) for x in A for y in B). Я считаю, что это действительно аккуратный способ сокращения вложенности в сложных for-loops, где понимание списка может быть неадекватным. Однако, прежде чем продолжить, я хотел проверить, как это работает с альтернативными методами. Вот несколько тестов, которые я провел. Использовал встроенный в Jupyter Notebook %%timeit для измерения производительности.

case-1: понимание ванильного списка

%%timeit -n 50 -r 5

[(x,y) for x in range(1000) if x%2==0 for y in range(1000) if y%2==1]
>>> 35.8 ms ± 1.3 ms per loop (mean ± std. dev. of 5 runs, 50 loops each)

case-2: itertools.product в понимании списка

Удален itertools импорт, чтобы избежать показа здесь времени импорта.

%%timeit -n 50 -r 5

[(x,y) for (x,y) in itertools.product(range(1000), range(1000)) if x%2==0 and y%2==1]
>>> 62.1 ms ± 1.16 ms per loop (mean ± std. dev. of 5 runs, 50 loops each)

case-3: ваниль, вложенная в l oop

%%timeit -n 50 -r 5

lst = []
for x in range(1000):
    for y in range(1000):
       if x%2 == 0 and y%2 == 1:
           lst.append((x,y))
>>> 72 ms ± 769 µs per loop (mean ± std. dev. of 5 runs, 50 loops each)

case-4: для l oop с itertools.product

%%timeit -n 50 -r 5

lst = []
for x, y in itertools.product(range(1000),range(1000)):
    if x%2==0 and y%2==1:
        lst.append((x,y))
>>> 74.5 ms ± 2.13 ms per loop (mean ± std. dev. of 5 runs, 50 loops each)

Однако эта часть документации, как я полагаю, требует лучшей производительности, чем ваниль for петли. Кроме того, case-2 не должен быть быстрее, чем case-1 ? В case-3 и case-4 разница в производительности ухудшается для itertools.product при увеличении размеров итерируемых элементов. Что здесь происходит? Кроме того, добавьте несколько примеров, где itertools.product может быть лучшим кандидатом, чем listcomp, или вложенным в циклы.

1 Ответ

2 голосов
/ 26 апреля 2020

Вы сравниваете разные вещи:

[(x,y) for x in range(1000) if x%2==0 for y in range(1000) if y%2==1]

... не то же самое, что

[(x,y) for x in range(1000) for y in range(1000) if x%2==0 and y%2==1]

Первый пропускает второй l oop полностью, если x%2 != 0 второй перебирает все комбинации 1000 ** 2 == 1,000,000. Случаи со 2 по 4 относятся к той же категории, что и второе понимание, поэтому они по сути медленнее.

...