Найти - Пользователи
Полная версия: loop using map(), непонятки со скоростью выполнения
Начало » Python для новичков » loop using map(), непонятки со скоростью выполнения
1
amdlintuxos
Добрый вечер.

import os, time
from random import randint

class ob():
def __init__(self, (x,y)):
self.xy = (x,y)

def render(self):
(x, y) = self.xy # some load
x*x # some load

def renderGlobal(self):
(x, y) = self.xy # some load
x*x # some load


if __name__ == '__main__':
npc_list = []

i = 0
while i < 1000:
npc = ob((randint(1,100), randint(1,100)))
npc_list.append(npc)

i += 1

obr = ob.render
for case in [0,1,2,3]:
if case == 0:
t = time.time()
print "case 0: classic FOR loop"
for n in npc_list:
n.render()
print "Time taken:", time.time() - t

if case == 1:
t = time.time()
print "case 1: loop using map()"
map(renderGlobal, npc_list)
print "Time taken:", time.time() - t

if case == 2:
t = time.time()
print "case 2: loop using map()"
map(ob.render, npc_list)
print "Time taken:", time.time() - t

if case == 3:
t = time.time()
print "case 3: loop using map()"
map(obr, npc_list)
print "Time taken:", time.time() - t
Результат выполнения кода
case 0: classic FOR loop
Time taken: 0.000545978546143
case 1: loop using map()
Time taken: 0.000361204147339
case 2: loop using map()
Time taken: 0.00108885765076
case 3: loop using map()
Time taken: 0.00101208686829
Непонятно:
a) почему case 2,3(map()) медленнее case 0 (for)
b) почему case 1(map()) так отрывается вперёд от case 2,3(map()), ведь в case 1 исползуется глобальная функция.
Вообще-то я думал что эффект наличия точек не сильно сказуется на работе map() функции. case 2 и case 3 вродебы подтверждают это практически, однако почему case 1 быстрее, и даже обычный for даёт фору некоторым map(), немогу разобрать.

Очень хотелось бы разобраться для понимания.
За помощь зараннее благодарю.
Андрей Светлов
1. Измеряйте дольше, в несколько итераций. Лучше, если получающиеся времена ближе к секундам, чем к миллисекундам.
2. map возвращает список значений - подумайте над этим.
3. Делайте профилирование вашего _реального_ кода - этот пример всего лишь показывает, что все варианты примерно равноценны, не стоит заморачиваться.
Усложните .render и увидите, где настоящие проблемы.
amdlintuxos
Андрей Светлов. спасибо за ответ.

Андрей Светлов
1. Измеряйте дольше, в несколько итераций. Лучше, если получающиеся времена ближе к секундам, чем к миллисекундам
со всем согласен, не учёл этого.
повторил, результат 5-ой итерации, счётчик цикла увеличен до 1000000
case 0: classic FOR loop
Time taken: 0.513402938843
case 1: loop using map()
Time taken: 0.385162115097
case 2: loop using map()
Time taken: 1.02443599701
case 3: loop using map()
Time taken: 1.02541089058
но тем не менее соотношение скоростей приблизительно такое же

Андрей Светлов
2. map возвращает список значений - подумайте над этим.
я этого изначально не учёл, спасибо. однако всёравно с учётом этого непонятен феноменальный отрыв case 1 по отношению к case 2,3. везде в этих case используется map.
Итого:
на вопрос a) почему case 2,3(map()) медленнее case 0 (for), похоже я ответ получил, спасибо
однако остаётся непонятен вариант
b) почему case 1(map()) так отрывается вперёд от case 2,3(map()), ведь в case 1 исползуется глобальная функция.
Андрей Светлов
3. Делайте профилирование вашего _реального_ кода - этот пример всего лишь показывает, что все варианты примерно равноценны, не стоит заморачиваться.
Усложните .render и увидите, где настоящие проблемы.
Так и сделаю, часто встречал такую рекомендацию и я её разделяю. Однако человек я такой, иногда клинит на мелочах, хочется понять хоть это и не несет особой смысловой нагрузки, просто интересно. Циклы используются очень часто, даже если с каждого получу мизерный выигрышь, буду этому безмерно рад, думаю лишним не будет.
Андрей Светлов
Вызов unbound method непомерно дорогой. bound method - хорошая оптимизация, а unbound - как видим, дорогая.
Не зря его выкинули, на 3.1 картина ровная и ожидаемая.
import os, time
from random import randint

class ob():
def __init__(self, (x,y)):
self.xy = (x,y)

def render(self):
(x, y) = self.xy # some load
x*x # some load

def renderGlobal(self):
(x, y) = self.xy # some load
x*x # some load

def check():
npc_list = [ob((randint(1,100), randint(1,100))) for i in xrange(1000)]

COUNT = 1000

obr = ob.render

t = time.time()
print "case 0: classic FOR loop"
for i in xrange(COUNT):
for n in npc_list:
n.render()
print "Time taken:", time.time() - t

t = time.time()
print "case 1: loop using map()"
for i in xrange(COUNT):
map(renderGlobal, npc_list)
print "Time taken:", time.time() - t

t = time.time()
print "case 2: loop using map()"
for i in xrange(COUNT):
map(ob.render, npc_list)
print "Time taken:", time.time() - t

t = time.time()
print "case 3: loop using map()"
for i in xrange(COUNT):
map(obr, npc_list)
print "Time taken:", time.time() - t

t = time.time()
print "case 4: loop with global function"
for i in xrange(COUNT):
for n in npc_list:
renderGlobal(n)
print "Time taken:", time.time() - t

t = time.time()
print "case 5: loop with global function stored in local var"
rg = renderGlobal
for i in xrange(COUNT):
for n in npc_list:
rg(n)
print "Time taken:", time.time() - t

t = time.time()
print "case 6: loop with unbound method"

for i in xrange(COUNT):
for n in npc_list:
obr(n)
print "Time taken:", time.time() - t

if __name__ == '__main__':
check()
import dis
# dis.dis(check)
Isem
Андрей Светлов
2. map возвращает список значений - подумайте над этим.
В 3.1 возвращается <map object>, по сути - итератор (или, точнее, генератор). Как, и range(…), например (то есть не список).
Это для информации, может быть кто-то не знает, а это важно с точки зрения оптимизации кода.
amdlintuxos
Всем спасибо за советы и поправки, оставлюка я и правда всю оптимизацию на потом., если в таковой буду нуждаться.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB