了解Python配置文件输出

时间:2009-09-24 03:50:00

标签: python profiling profile

我正在尝试使用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)

6 个答案:

答案 0 :(得分:6)

是的,我也遇到过同样的问题。

我知道解决这个问题的唯一方法是将大函数包装成几个较小的函数调用。这将允许探查器考虑每个较小的函数调用。

有趣的是,这样做的过程(对我来说无论如何)使得低效率显而易见,因此我甚至不必运行分析器。

答案 1 :(得分:5)

我已经看过你的代码,看起来你做了很多函数调用和属性查找,作为“检查”的一部分,或者在跳跃之前展望未来。您还有许多专用于跟踪相同条件的代码,即许多代码用于创建“唯一”ID。

而不是试图为每个选票分配某种独特的字符串,不是吗 使用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(来自collections模块)。 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>])]

答案 2 :(得分:4)

我在我的代码中使用了this decorator,它帮助我完成了pyparsing调优工作。

答案 3 :(得分:4)

Profilers可以是这样的。我使用的方法是this。它很快就能解决问题的核心。

答案 4 :(得分:3)

我会支持弗拉格斯沃思,说你要将你的功能分成更小的功能。

话虽如此,你正在正确地阅读输出:tottime是值得观看的。

现在你的减速可能是:

由于似乎有100000次调用appendBallot,并且没有任何明显的循环,我建议它在你的断言中。因为你正在执行:

assert(ballotID not in self.ballotIDs)

这实际上就是一个循环。因此,第一次调用此函数时,它将遍历一个(可能为空)数组,然后断言是否找到该值。它将遍历整个阵列的第100000次。

这里确实存在一个可能的错误:如果选票被删除,那么添加的下一张选票将与最后添加的选票具有相同的ID(除非是删除的选票)。我认为你最好使用一个简单的计数器。这样你就可以在每次添加选票时增加它。或者,您可以使用UUID获取唯一ID。

或者,如果您正在查看某种程度的持久性,请使用ORM,并让它进行ID生成,并为您进行独特检查。

答案 5 :(得分: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是一个列表,因此assert语句将导致二次行为。由于您没有为您的数据结构提供任何文档,因此不可能是规定性的,但如果外观顺序无关紧要,您可以使用集合而不是列表。

其次,逻辑(在没有关于什么是ballotID的文件的情况下,以及什么是非-obototID arg的意思)似乎是严重错误的:

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 - 它更快,看起来更好。

您可能会考虑审核您的数据结构......它们似乎略微是巴洛克式的;构建它们可能需要相当多的CPU时间。