理解Python配置文件输出

7 投票
6 回答
9132 浏览
提问于 2025-04-15 14:33

我正在尝试使用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 个回答

5

我支持Fragsworth的观点,你应该把你的函数拆分成更小的部分。

说到这里,你的输出读得没错:tottime是最值得关注的部分。

现在来看看你可能出现慢的地方:

因为你似乎调用了100000次appendBallot,而且没有明显的循环,我建议问题可能出在你的assert上。因为你执行的是:

assert(ballotID not in self.ballotIDs)

这实际上会像一个循环。因此,当你第一次调用这个函数时,它会遍历一个(可能是空的)数组,然后检查值是否存在。而到了第100000次,它会遍历整个数组。

这里其实还有一个可能的bug:如果一个选票被删除,下一个添加的选票会和最后添加的那张有相同的ID(除非删除的是那张)。我觉得你最好用一个简单的计数器。这样每次添加选票时只需增加一次计数。或者,你可以使用UUID来生成唯一的ID。

另外,如果你想要一些持久化的功能,可以使用ORM,让它为你处理ID的生成和唯一性检查。

7

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

我知道的解决办法就是把你那个大函数拆分成几个小函数。这样,分析工具就能分别考虑每个小函数的运行情况。

有趣的是,做这个过程的时候(对我来说),我很容易就发现了哪些地方效率低下,所以我甚至都不需要运行分析工具。

5

性能分析工具有时候就是这样。我用的方法是这个。它能迅速找到问题的关键所在。

撰写回答