2009-09-24 73 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一起運行,它們將不會被執行, – 2009-09-24 23:22:02

+0

感謝所有有用的答案。 – 2009-09-25 01:45:42

+0

Python分析器不忽略'assert'/statements /,而不是忽略方法中的所有其他/ statements /。編寫'assert(表達式)'而不是'assert expression'不會使它變成一個可以不受限制的函數調用。 – 2009-09-25 04:36:42

回答

3

分析器可以是這樣的。我使用的方法是this。它很快就會解決問題的核心。

+0

雖然有很多好評,但這是爲我所需要的最快最簡單的答案。 – 2009-09-27 11:40:30

+0

@Jeff。我很高興它有幫助。 – 2009-09-27 13:38:30

+0

@George:我知道你的意思,但它有點大。如果鏈接死亡,我會處理它。 – 2013-06-21 14:48:10

6

是的,我跨同樣的問題來爲好。

我知道解決這個問題的唯一方法是將你的大函數包裝成幾個較小的函數調用。這將允許分析器考慮每個較小的函數調用。

有趣的是,這樣做的過程(無論如何)使得它顯而易見的效率低下,所以我甚至不必運行分析器。

+0

你是對的(在實際意義上),但「重寫代碼的方式不同,因此分析器的缺陷不太明顯」似乎是錯誤的答案。 – Basic 2013-11-05 11:30:46

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(從集合模塊)來處理一些您的後顧之憂關於字典缺少一個給定的鍵 。 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 2009-09-24 07:10:31

3

我會支持Fragsworth說你想把你的功能拆分成更小的功能。

話雖如此,您正在正確讀取輸出:tottime是一個要觀看的。

現在,因爲你的經濟放緩很可能是:

由於似乎設爲100000個呼叫appendBallot,並沒有任何明顯的循環,我建議它在你的斷言。因爲您正在執行:

assert(ballotID not in self.ballotIDs) 

這實際上會充當循環。因此,當你第一次調用這個函數時,它會遍歷一個(可能是空的)數組,然後斷言是否找到了這個值。第100000次它將遍歷整個數組。

這裏實際上有一個可能的錯誤:如果刪除選票,那麼添加的下一個選票將具有與最後添加的選票相同的標識(除非那是刪除的選票)。我認爲你最好使用簡單的計數器。這樣你可以在每次添加選票時增加它。或者,您可以使用UUID獲取唯一的ID。

或者,如果您正在尋找某種程度的持久性,請使用ORM並讓它執行ID生成以及針對您的唯一檢查。

+0

他不是在「叫」任何東西;斷言是一個聲明。 – 2009-09-25 04:38:32

+0

我的不好。將解決。 – 2009-09-25 05:57:27

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 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時間。