生成器函数性能
我正在尝试理解生成器函数的性能。我使用了 cProfile 和 pstats 模块来收集和检查性能数据。这里讨论的函数是:
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
是一个 Unicode 文本字符串,self.stringEnd
是一个包含 4 个简单正则表达式的字典,self.patt
是一个大的正则表达式。整个过程是将这个大字符串一个一个地拆分成小字符串。
在分析使用这个函数的程序时,我发现程序运行时间的大部分都花在了这个函数上:
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__)
但是查看这个函数本身的性能分析时,发现它的子函数调用并没有花费太多时间:
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}
这个函数的其他部分除了循环、赋值和条件判断外,没什么特别的。即使我使用的生成器的 send
方法也很快:
ncalls tottime percall cumtime percall filename:lineno(function)
13643/10835 0.007 0.000 11.552 0.001 {method 'send' of 'generator' objects}
难道说 yield
,也就是把一个值返回给调用者,花费了大部分时间吗?!还有其他我不知道的原因吗?
编辑:
我可能应该提到,生成器函数 __iter__
是一个小类的方法,所以 self
指的是这个类的一个实例。
2 个回答
1
如果我理解你的例子没错的话,你是把一个生成器对象放进了delimiter
里,然后用它来查找数组。这可能不是你速度慢的原因,但我敢肯定这是一处错误。