Sto riscontrando differenze di prestazioni abbastanza sorprendenti quando si esegue l'iterazione su un piccolo contenitore con un iteratore personalizzato. Speravo che qualcuno potesse aiutarmi a capire da dove vengono queste differenze.performance iteratore personalizzata
Primo contesto; Sto scrivendo un numero di moduli di estensione Python usando boost :: python, uno dei quali contiene un binding a un tipo di vettore float 3d che implementa il getitem. Dal momento che ha ottenuto la possibilità di iterare su di esso, tuttavia sembra abbastanza lento, ma non è ovvio perché così ho deciso di giocare con alcuni semplici iteratori personalizzati in python per avere un'idea migliore di come funzionano le cose. Che è dove questi iteratori provenivano da ...
class MyIterator1(object):
__slots__ = ['values', 'popfn']
def __init__(self):
self.values = ['x', 'y', 'z']
self.popfn = self.values.pop
def __length_hint__(self):
return 3
def __iter__(self):
return self
def next(self):
try:
return self.popfn()
except IndexError:
raise StopIteration
class MyIterator2(object):
__slots__ = ['values', 'itfn']
def __init__(self):
self.values = ['x', 'y', 'z']
it = iter(self.values)
self.itfn = it.next
def __length_hint__(self):
return 3
def __iter__(self):
return self
def next(self):
return self.itfn()
class MyIterator3(object):
__slots__ = ['values', 'i']
def __init__(self):
self.values = ['x', 'y', 'z']
self.i = 0
def __length_hint__(self):
return 3
def __iter__(self):
return self
def next(self):
if self.i >= 3:
raise StopIteration
value = self.values[self.i]
self.i += 1
return value
def MyIterator4():
val = ['x', 'y', 'z']
yield val[0]
yield val[1]
yield val[2]
successivo mi sono imbattuto questi attraverso uno script con il modulo timeit (che assume il codice di cui sopra è in un modulo chiamato testiter)
import timeit
timer1 = timeit.Timer('r = list(testiter.MyIterator1())', 'import testiter')
timer2 = timeit.Timer('r = list(testiter.MyIterator2())', 'import testiter')
timer3 = timeit.Timer('r = list(testiter.MyIterator3())', 'import testiter')
timer4 = timeit.Timer('r = list(testiter.MyIterator4())', 'import testiter')
timer5 = timeit.Timer('r = list(iter(["x", "y", "z"]))', 'import testiter')
print 'list(testiter.MyIterator1())'
print timer1.timeit()
print "\n"
print 'list(testiter.MyIterator2())'
print timer2.timeit()
print "\n"
print 'list(testiter.MyIterator3())'
print timer3.timeit()
print "\n"
print 'list(testiter.MyIterator4())'
print timer4.timeit()
print "\n"
print 'list(iter(["x", "y", "z"]))'
print timer5.timeit()
Questo stampa il seguente
list(testiter.MyIterator1())
8.5735929
list(testiter.MyIterator2())
5.28959393501
list(testiter.MyIterator3())
6.11230111122
list(testiter.MyIterator4())
2.31263613701
list(iter(["x", "y", "z"]))
1.26243281364
Non sorprende che il listenerator python sia il più veloce, con un margine. Presumo che questo dipenda da alcune ottimizzazioni magiche all'interno di python. Il generatore è anche considerevolmente più veloce delle classi MyIterator, che di nuovo non ne sono molto sorpreso, e presumo sia dovuto a tutto il lavoro che si sta facendo in c, ma questo è solo un'ipotesi. Ora gli altri sono più confusi/supplichevoli. Le dichiarazioni try/except sono costose come sembrano in questo contesto o qualcos'altro?
Qualsiasi aiuto nello spiegare queste differenze sarebbe molto apprezzato! Ci scusiamo per il lungo post.
Si può provare a usare tupla invece di ['x', 'y', 'z'] (dove possibile): la costruzione di tuple è leggermente più veloce di quella di lista. –