Производительность функции генератора - PullRequest
6 голосов
/ 08 июня 2011

Я пытаюсь понять производительность функции генератора. Я использовал 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 - текстовая строка в юникоде, 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, присваивания и 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, передавая значение обратно потребителю, отнимает большую часть времени ?! Что-нибудь еще, о чем я не знаю?

EDIT

Я, вероятно, должен был упомянуть, что функция генератора __iter__ является методом небольшого класса, поэтому self относится к экземпляру этого класса.

Ответы [ 2 ]

2 голосов
/ 09 июня 2011

На самом деле это ответ Dunes , который, к сожалению, только дал его в качестве комментария и, похоже, не склонен дать ему правильный ответ.

Основным виновником исполнения стали кусочки струн. Некоторые измерения времени показали, что производительность среза заметно ухудшается с большими срезами (что означает получение большого среза из уже большой строки). Чтобы обойти это, я теперь использую параметр pos для методов объекта regex:

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

Спасибо всем , которые помогли.

1 голос
/ 08 июня 2011

Если вы читаете ваш пример правильно, вы берете объект генератора, помещаете его в delimiter и используете его для поиска в массиве. Возможно, это не ваша проблема со скоростью, но я уверен, что это ошибка.

...