2009-09-24 4 views
6

Я пытаюсь использовать профилировщик Python для ускорения моего кода. Я смог определить конкретную функцию, где почти все время тратится, но я не могу понять, где в этой функции время расходуется.Понимание профиля профиля Python

Ниже у меня есть профиль, который показывает, что «appendBallot» является основным виновником и потребляет почти 116 секунд. Далее ниже, у меня есть код для «appendBallot».

Я не могу понять из вывода профиля, какую часть «appendBallot» мне нужно оптимизировать, поскольку следующая самая высокая запись времени меньше секунды. Я уверен, что многие из вас могли бы рассказать мне только из моего кода, но я хотел бы понять, как получить эту информацию из вывода профиля. Любая помощь будет принята с благодарностью.

выход профиля:

ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 116.168 116.168 <string>:1(<module>) 
     1 0.001 0.001 116.168 116.168 {execfile} 
     1 0.003 0.003 116.167 116.167 foo.py:1(<module>) 
     1 0.000 0.000 116.139 116.139 ballots.py:330(loadKnown) 
     1 0.000 0.000 116.109 116.109 plugins.py:148(load) 
     1 0.196 0.196 116.108 116.108 BltBallotLoader.py:37(loadFile) 
    100000 114.937 0.001 115.912 0.001 ballots.py:133(appendBallot) 
    100000 0.480 0.000 0.790 0.000 ballots.py:117(newBallot) 
    316668 0.227 0.000 0.310 0.000 ballots.py:107(getNumCandidates) 
417310/417273 0.111 0.000 0.111 0.000 {len} 
    200510 0.071 0.000 0.071 0.000 {method 'append' of 'list' objects} 
    99996 0.045 0.000 0.045 0.000 {method 'add' of 'set' objects} 
    100000 0.042 0.000 0.042 0.000 {method 'has_key' of 'dict' objects} 
     1 0.000 0.000 0.030 0.030 plugins.py:202(getLoaderPluginClasses) 
     1 0.000 0.000 0.030 0.030 plugins.py:179(getPluginClasses) 
     1 0.000 0.000 0.030 0.030 plugins.py:205(getLoaderPluginClass) 
     3 0.016 0.005 0.029 0.010 {__import__} 
     1 0.022 0.022 0.025 0.025 ballots.py:1(<module>) 
     1 0.010 0.010 0.013 0.013 BltBallotLoader.py:1(<module>) 
     7 0.000 0.000 0.003 0.000 re.py:227(_compile) 

Код:

def appendBallot(self, ballot, ballotID=None): 
    "Append a ballot to this Ballots object." 

    # String representation of ballot for determining whether ballot is unique 
    ballotString = str(list(ballot)) 

    # Ballot as the appropriate array to conserve memory 
    ballot = self.newBallot(ballot) 

    # Assign a ballot ID if one has not been given 
    if ballotID is None: 
     ballotID = len(self.ballotIDs) 
    assert(ballotID not in self.ballotIDs) 
    self.ballotIDs.append(ballotID) 

    # Check to see if we have seen this ballot before 
    if self.uniqueBallotsLookup.has_key(ballotString): 
     i = self.uniqueBallotsLookup[ballotString] 
     self.uniqueBallotIDs[i].add(ballotID) 
    else: 
     i = len(self.uniqueBallots) 
     self.uniqueBallotsLookup[ballotString] = i 
     self.uniqueBallots.append(ballot) 
     self.uniqueBallotIDs.append(set([ballotID])) 
    self.ballotOrder.append(i) 
+0

Это действительно утверждение(), которое постоянно держится. Интересно, игнорирует ли профайлер Python команды assert(), поскольку они не будут выполняться, если код запущен с -O, –

+0

Спасибо за все полезные ответы. –

+0

Профайлер Python не игнорирует 'assert'/statements/больше, чем игнорирует все остальные/statements/в методе. Написание 'assert (выражение)' вместо просто 'assert expression' не превращает его в вызов функции, который может быть невозможен. –

ответ

3

Профилировщики могут быть такими. Используемый метод: this. Он сразу же получает право на сердце проблемы.

+0

Хотя было много хороших комментариев, это тот, который обеспечил самый быстрый и легкий ответ на то, что мне нужно. –

+0

@Jeff. Я рад, что это помогло. –

+0

@George: Я знаю, что вы имеете в виду, но он немного большой. Если связь погибнет, я буду заниматься этим. –

6

Да, я наткнулся на ту же проблему, как хорошо.

Единственный способ, которым я знаю, это обернуть вашу большую функцию несколькими меньшими вызовами функций. Это позволит профилировщику учитывать каждый из меньших вызовов функций.

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

+0

Вы правы (в практическом смысле), и все же «переписываете свой код по-другому, чтобы недостатки профайлера были менее очевидными» кажется неправильным ответом. – Basic

5

Я взглянул на ваш код, и похоже, что вы делаете много вызовов функций и поиска атрибутов как часть вашей проверки или смотря вперед, прежде чем прыгать. У вас также есть много кода, предназначенных для отслеживания одного и того же состояния, т. Е. Много бит кода, который ищет создание уникальных идентификаторов.

вместо того, чтобы пытаться присвоить какую-то уникальную строку для каждого голосования, не могли бы вы использовать ballotID (целое число?)

теперь вы могли бы иметь словарь (uniqueBallotIDs) отображение ballotID и тому фактический объект голосования.

процесс может быть что-то вроде этого:

def appendBallot(self, ballot, ballotID=None): 
    if ballotID is None: 
     ballotID = self._getuniqueid() # maybe just has a counter? up to you. 
    # check to see if we have seen this ballot before. 
    if not self._isunique(ballotID): 
     # code for non-unique ballot ids. 
    else: 
     # code for unique ballot ids. 

    self.ballotOrder.append(i) 

Вы можете быть в состоянии справиться с некоторыми из ваших забот о словаре отсутствует данный ключ с помощью defaultdict (из модуля коллекций). collection docs

Редактировать для полноты я буду включать в себя использование образца в defaultdict:

>>> from collections import defaultdict    

>>> ballotIDmap = defaultdict(list) 
>>> ballotID, ballot = 1, object() # some nominal ballotID and object. 
>>> # I will now try to save my ballotID. 
>>> ballotIDmap[ballotID].append(ballot) 
>>> ballotIDmap.items() 
[(1, [<object object at 0x009BB950>])] 
4

я использовал this decorator в моем коде, и это помогло мне с моей настройкой Pyparsing работой.

+0

Это выглядит хорошо. – Fragsworth

3

Я поддержу Fragsworth, сказав, что вы захотите разделить свою функцию на более мелкие.

Сказав это, вы правильно читаете результат: тоталь - это тот, который нужно посмотреть.

Теперь, когда ваш спад, вероятно, будет:

Так, кажется, 100000 звонков в appendBallot, и нет никаких очевидных петель, я хотел бы предложить это в вашем утверждают. Потому что вы выполняете:

assert(ballotID not in self.ballotIDs) 

Это будет действовать как петля. Таким образом, в первый раз, когда вы вызываете эту функцию, она будет перебирать через (возможно пустой) массив и затем утверждать, если значение было найдено. В 100000-й раз он будет проходить через весь массив.

И на самом деле существует возможная ошибка: если бюллетень удален, то добавленный бюллетень будет иметь тот же идентификатор, что и последний добавленный (если только это не было удалено). Я думаю, вам будет лучше использовать простой счетчик. Таким образом, вы можете просто увеличивать его каждый раз, когда вы добавляете бюллетень. Кроме того, вы можете использовать UUID для получения уникальных идентификаторов.

В качестве альтернативы, если вы ищете некоторый уровень настойчивости, используйте ORM и получите его для создания идентификатора и уникальной проверки для вас.

+0

Он ничего не «зовет»; Утверждение - это утверждение. –

+0

Мой плохой. Починю. –

2

У вас есть две проблемы в этой небольшой кусочек кода:

# Assign a ballot ID if one has not been given 
if ballotID is None: 
    ballotID = len(self.ballotIDs) 
assert(ballotID not in self.ballotIDs) 
self.ballotIDs.append(ballotID) 

Во-первых, оказывается, что self.ballotIDs список, так что оператор утверждают вызовет квадратное поведение. Поскольку вы не указали какую-либо документацию для своих структур данных, невозможно быть предписывающим, но если порядок внешнего вида не имеет значения, вы можете использовать набор вместо списка.

Во-вторых, логика (при отсутствии документации на то, что ballotID все о, и то, что не-Ни ballotID аргумент не означает), кажется, серьезно прослушиваться:

obj.appendBallot(ballota, 2) # self.ballotIDs -> [2] 
obj.appendBallot(ballotb) # self.ballotIDs -> [2, 1] 
obj.appendBallot(ballotc) # wants to add 2 but triggers assertion 

Другие комментарии:

Вместо adict.has_key(key) используйте key in adict - это быстрее и выглядит лучше.

Возможно, вам захочется рассмотреть ваши структуры данных ... они кажутся слегка барочными; может быть справедливая бит процессорного времени, связанного с их построением.

Смежные вопросы