理解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)
6 个回答
我支持Fragsworth的观点,你应该把你的函数拆分成更小的部分。
说到这里,你的输出读得没错:tottime是最值得关注的部分。
现在来看看你可能出现慢的地方:
因为你似乎调用了100000次appendBallot,而且没有明显的循环,我建议问题可能出在你的assert上。因为你执行的是:
assert(ballotID not in self.ballotIDs)
这实际上会像一个循环。因此,当你第一次调用这个函数时,它会遍历一个(可能是空的)数组,然后检查值是否存在。而到了第100000次,它会遍历整个数组。
这里其实还有一个可能的bug:如果一个选票被删除,下一个添加的选票会和最后添加的那张有相同的ID(除非删除的是那张)。我觉得你最好用一个简单的计数器。这样每次添加选票时只需增加一次计数。或者,你可以使用UUID来生成唯一的ID。
另外,如果你想要一些持久化的功能,可以使用ORM,让它为你处理ID的生成和唯一性检查。
是的,我也遇到过同样的问题。
我知道的解决办法就是把你那个大函数拆分成几个小函数。这样,分析工具就能分别考虑每个小函数的运行情况。
有趣的是,做这个过程的时候(对我来说),我很容易就发现了哪些地方效率低下,所以我甚至都不需要运行分析工具。
性能分析工具有时候就是这样。我用的方法是这个。它能迅速找到问题的关键所在。