发电机功能性能

时间:2011-06-08 18:07:06

标签: python performance profiling generator

我正在尝试了解生成器功能的性能。我使用了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}

除了while,assignment和if-else之外,函数的其余部分并不多。甚至我使用的生成器上的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 个答案:

答案 0 :(得分:2)

这实际上是Dunes的答案,不幸的是,他只是将其作为评论而不是倾向于将其置于正确的答案中。

主要表现的罪魁祸首是字符串切片。一些定时测量表明,切片性能会随着大切片而明显降低(意味着从已经很大的切片中取出一大片切片)。为了解决这个问题,我现在使用pos参数来表示正则表达式对象方法:

    if delimiter:
        mo = self.stringEnd[delimiter].search(inData, pos=cursor)
    else:
        mo = self.patt.match(inData, pos=cursor)

感谢all帮助过。

答案 1 :(得分:1)

如果正确读取样本,您将获取生成器对象,将其放入delimiter,并将其用于数组查找。这可能不是你的速度问题,但我很确定这是一个错误。