Предположим, что существует класс A и есть список экземпляров класса A, называемый lst
.Почему моя функция «xmap» не быстрее, чем встроенная «карта»?
Предположим, что мы хотим, чтобы вызвать определенный метод m
, класса А миллионы и миллионы раз снова и снова на каждом конкретном случае в нашем списке, (практический пример: entity.update()
метод в цикле игры). Мы знаем, что простой способ сделать это:
for obj in lst: obj.m()
Однако такой код заставляет нас спать. Таким образом, мы считаем, что использовать map
следующим образом:
map(lambda obj: obj.m(), lst)
Но мы проведем несколько тестов времени на строке выше кода и оказывается, что это гораздо медленнее, чем наш простой for
цикла. Иногда это даже в 2 раза медленнее. Затем мы думаем про себя: «Хм, возможно, медленнее, потому что map
строит список возвращаемых значений для всех вызовов функций и возвращает этот список».
Предположим, что мы черпаем вдохновение из ленивой и эффективной с точки зрения памяти встроенной функции, называемой xrange
. По большей части мы думаем о себе, это более холодная версия range
. Таким образом, мы определяем функцию под названием xmap
, которая просто применяет функцию к списку объектов без, строя список возвращаемых значений и возвращая его. Реализация заключается в следующем:
def xmap(func, lst):
for obj in lst: func(obj)
Довольно круто, потому что эта функция просто выполняет for
цикл выше, только это позволяет нам оставаться фантазии и отправить в наши функции лямбда. Мы считаем, что это идеальный компромисс. Но мы тщательны и осторожны, поэтому мы решили сделать 2 скрипта, чтобы проверить скорость нашего кода, чтобы увидеть, действительно ли мы сделали его быстрее, чем map
.
Наш первый скрипт будет просто использовать map
и бесполезно построить список, который нам даже не нужен.
script1.py
:
class A:
def m(self):
pass
lst = [A() for i in xrange(15)]
import time
start = time.time()
for i in xrange(1000000):
map(lambda obj: obj.m(), lst)
print time.time()-start, 'seconds'
Наш второй сценарий будет использовать xmap
, и мы считаем, , что это будет быстрее, потому что не нужно будет составить список 15 возвращаемых значений 1000000 раз и вернуть его.
script2.py
def xmap(func, lst):
for obj in lst: func(obj)
class A:
def m(self):
pass
lst = [A() for i in xrange(15)]
import time
start = time.time()
for i in xrange(1000000):
xmap(lambda obj: obj.m(), lst)
print time.time()-start, 'seconds'
Наконец, мы сделали и несколько взволнован, чтобы видеть, насколько быстрее наш код будет. Однако после запуска обоих сценариев несколько раз друг против друга получается, что script2.py
не выглядит быстрее, чем script1.py
. На самом деле, как выясняется, script2.py
иногда занимает еще больше времени, чем script1.py
. xmap
, кажется, принимает более или менее такое же количество времени, что и map
.
Почему я получил эти результаты?
C:\dev\py>python script1.py
14.7799999714 seconds
C:\dev\py>python script2.py
14.2170000076 seconds
C:\dev\py>python script1.py
12.1800000668 seconds
C:\dev\py>python script2.py
12.5759999752 seconds
C:\dev\py>python script1.py
14.3020000458 seconds
C:\dev\py>python script2.py
14.9490001202 seconds
C:\dev\py>python script1.py
14.6879999638 seconds
C:\dev\py>python script2.py
14.3139998913 seconds
Я думал, что я бы, по крайней мере оптимизировали что-то из map
, потому что я не строит список возвращаемых значений, но мой код не будет быстрее. Я знаю, за то, что список строительство занимает некоторое время, потому что я сделал следующее:
>>> import timeit
>>> timeit.timeit('[]')
0.6
>>> timeit.timeit('[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]')
0.6807682686160632
>>> timeit.timeit('[None,None,None,None,None,None,None,None,None,None,None,None,
None,None,None]')
0.7460120889200539
Так почему не кажется, моя xmap
функции будет быстрее, чем map
?
Какова производительность простой петли без карты или «xmap»? – prgao
@prgao очень очень быстро. Быстрее, чем использовать 'map' или' xmap'. – Shashank