2013-09-25 3 views
1

Предположим, что существует класс 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?

+0

Какова производительность простой петли без карты или «xmap»? – prgao

+0

@prgao очень очень быстро. Быстрее, чем использовать 'map' или' xmap'. – Shashank

ответ

7

Во-первых, и самое главное: синхронизация с микро-оптимизацией, подобная этому, только смущает вас, потому что вы измеряете слишком низкие накладные расходы на такие функции, как функции.

В этом случае, я уверен, проблема в том, что прибыль, которую вы можете получить от map/xmap, вы теряете из-за дополнительных lambda. Если вы используете A.m непосредственно вместо этого, все выглядит гораздо лучше:

>>> %timeit for obj in lst: obj.m() 
100000 loops, best of 3: 2.99 µs per loop 
>>> %timeit [obj.m() for obj in lst] 
100000 loops, best of 3: 3.5 µs per loop 
>>> %timeit xmap(lambda obj: obj.m(), lst) 
100000 loops, best of 3: 5.69 µs per loop 
>>> %timeit xmap(A.m, lst) 
100000 loops, best of 3: 3.32 µs per loop 

FWIW, он смотрит на меня, как в конечном итоге ваш xmap может выиграть:

>>> lst = [A() for i in xrange(10**3)] 
>>> %timeit for obj in lst: obj.m() 
1000 loops, best of 3: 198 µs per loop 
>>> %timeit [obj.m() for obj in lst] 
1000 loops, best of 3: 216 µs per loop 
>>> %timeit xmap(lambda obj: obj.m(), lst) 
1000 loops, best of 3: 353 µs per loop 
>>> %timeit xmap(A.m, lst) 
10000 loops, best of 3: 189 µs per loop 

, но я бы не взять эти цифры слишком серьезно либо ,

Когда вы говорите: «такой код (то есть простой для циклов) заставляет нас спать», я согласен - писать простые циклы означает, что вы закончили программирование намного быстрее и можете ложиться спать раньше.

3

карта выполнена в C, и работает в C-петлевой земли. Вы работаете в Python-loop-land.

+0

Если это так, то не должен ли мой 'xmap' быть намного медленнее, чем' map'? На данный момент кажется, что они работают с одинаковой скоростью. Кроме того, если петли Python настолько медленны, то почему «для obj в lst: obj.m()' так намного быстрее, чем 'map'? – Shashank

+1

Здесь происходят 2 разных события, с двумя разными сравнениями. Вы устраняете лямбду от одного сложного уровня сложности Lambdas до звонков на карту (вы выходите из C и обратно в python, а затем обратно в c). избегая того, что ускоряет работу. Вторая вещь здесь ..., которая является первой, которую вы воспитывали в своем комментарии ... В этом случае карта по-прежнему бесполезна, поэтому ее оптимизированная трата. – Tritium21

+0

Если это действительно так, то я думаю, что Python должен сделать что-то вроде 'xmap' встроенной функции, реализованной на C. – Shashank

Смежные вопросы