Быстро ли повторять итерацию небольшого списка в инструкции any()?
Рассмотрим следующую операцию в пределе итераций с малой длиной,
d = (3, slice(None, None, None), slice(None, None, None))
In [215]: %timeit any([type(i) == slice for i in d])
1000000 loops, best of 3: 695 ns per loop
In [214]: %timeit any(type(i) == slice for i in d)
1000000 loops, best of 3: 929 ns per loop
Настройка как list
на 25% быстрее, чем использование выражения генератора?
Почему это так, как установка list
является дополнительной операцией.
Примечание. В обоих забегах я получил предупреждение: The slowest run took 6.42 times longer than the fastest. This could mean that an intermediate result is being cached
I
Анализ
В этом конкретном тесте структуры list()
быстрее до длины 4
, из которой генератор имеет повышенную производительность.
Красная строка показывает, где это событие происходит, и черная линия показывает, где обе они равны по производительности.
Код занимает около 1 мин для работы на моем MacBook Pro, используя все ядра:
import timeit, pylab, multiprocessing
import numpy as np
manager = multiprocessing.Manager()
g = manager.list([])
l = manager.list([])
rng = range(1,16) # list lengths
max_series = [3,slice(None, None, None)]*rng[-1] # alternate array types
series = [max_series[:n] for n in rng]
number, reps = 1000000, 5
def func_l(d):
l.append(timeit.repeat("any([type(i) == slice for i in {}])".format(d),repeat=reps, number=number))
print "done List, len:{}".format(len(d))
def func_g(d):
g.append(timeit.repeat("any(type(i) == slice for i in {})".format(d), repeat=reps, number=number))
print "done Generator, len:{}".format(len(d))
p = multiprocessing.Pool(processes=min(16,rng[-1])) # optimize for 16 processors
p.map(func_l, series) # pool list
p.map(func_g, series) # pool gens
ratio = np.asarray(g).mean(axis=1) / np.asarray(l).mean(axis=1)
pylab.plot(rng, ratio, label='av. generator time / av. list time')
pylab.title("{} iterations, averaged over {} runs".format(number,reps))
pylab.xlabel("length of iterable")
pylab.ylabel("Time Ratio (Higher is worse)")
pylab.legend()
lt_zero = np.argmax(ratio<1.)
pylab.axhline(y=1, color='k')
pylab.axvline(x=lt_zero+1, color='r')
pylab.ion() ; pylab.show()
Ответы
Ответ 1
Захват - это размер элементов, которые вы применяете any
on. Повторите этот же процесс в более крупном наборе данных:
In [2]: d = ([3] * 1000) + [slice(None, None, None), slice(None, None, None)]*1000
In [3]: %timeit any([type(i) == slice for i in d])
1000 loops, best of 3: 736 µs per loop
In [4]: %timeit any(type(i) == slice for i in d)
1000 loops, best of 3: 285 µs per loop
Затем использование list
(загрузка всех элементов в память) становится намного медленнее, а выражение генератора играет лучше.