Ich versuche, die Leistung einer Generatorfunktion zu verstehen. Ich habe cProfile und das Modul pstats verwendet, um Profildaten zu sammeln und zu untersuchen. Die betreffende Funktion ist die folgende:
def __iter__(self):
delimiter = None
inData = self.inData
lenData = len(inData)
cursor = 0
while cursor < lenData:
if delimiter:
mo = self.stringEnd[delimiter].search(inData[cursor:])
else:
mo = self.patt.match(inData[cursor:])
if mo:
mo_lastgroup = mo.lastgroup
mstart = cursor
mend = mo.end()
cursor += mend
delimiter = (yield (mo_lastgroup, mo.group(mo_lastgroup), mstart, mend))
else:
raise SyntaxError("Unable to tokenize text starting with: \"%s\"" % inData[cursor:cursor+200])
self.inData
ist eine Unicode-Textzeichenfolge, self.stringEnd
ist ein Diktat mit 4 einfachen Regexen, self.patt ist ein großer Regex. Das Ganze dient dazu, die große Zeichenkette in kleinere Zeichenketten aufzuteilen, eine nach der anderen.
Beim Profiling eines Programms, das diese Funktion verwendet, habe ich festgestellt, dass der größte Teil der Programmlaufzeit in dieser Funktion verbracht wird:
In [800]: st.print_stats("Scanner.py:124")
463263 function calls (448688 primitive calls) in 13.091 CPU seconds
Ordered by: cumulative time
List reduced from 231 to 1 due to restriction <'Scanner.py:124'>
ncalls tottime percall cumtime percall filename:lineno(function)
10835 11.465 0.001 11.534 0.001 Scanner.py:124(__iter__)
Betrachtet man jedoch das Profil der Funktion selbst, so wird nicht viel Zeit mit den Unteraufrufen der Funktionen verbracht:
In [799]: st.print_callees("Scanner.py:124")
Ordered by: cumulative time
List reduced from 231 to 1 due to restriction <'Scanner.py:124'>
Function called...
ncalls tottime cumtime
Scanner.py:124(__iter__) -> 10834 0.006 0.006 {built-in method end}
10834 0.009 0.009 {built-in method group}
8028 0.030 0.030 {built-in method match}
2806 0.025 0.025 {built-in method search}
1 0.000 0.000 {len}
Der Rest der Funktion ist nicht viel mehr als while, Zuweisungen und if-else. Auch die send
Methode des Generators, die ich verwende, ist schnell:
ncalls tottime percall cumtime percall filename:lineno(function)
13643/10835 0.007 0.000 11.552 0.001 {method 'send' of 'generator' objects}
Ist es möglich, dass die yield
Der größte Teil der Zeit wird darauf verwendet, einen Wert an den Verbraucher zurückzugeben?! Gibt es sonst noch etwas, das mir nicht bekannt ist?
EDITAR :
Ich hätte vielleicht erwähnen sollen, dass die Generatorfunktion __iter__
ist eine Methode einer kleinen Klasse, also self
verweist auf eine Instanz dieser Klasse.