2012-07-17 11 views
0

所以,首先。這是我的服務器引擎。 gilmud.py!好吧,這是我以前的小說大小的文章的縮短版本。Python太慢了 - 我可以在低級別和cp腳本中重寫引擎嗎?

上面的鏈接是我們的MUD的python服務器引擎。注意到在

self.tickThread = threading.Thread(None, self.runTicks, None,()) 
self.tickThread.start() 

線73-75,和359 ...

def runTicks(self): 
    while self.running: 
     time.sleep(.1) 
     for thing in Thing.List.values(): 
      if thing: 
       if "person" in thing.attrs: 
        if "spawner" in thing.attrs: 
         thing.tick() 

,你可能會看到給我們所需要的大致100位的選手和2000小怪/籌備了可怕的方法「生活'。 tick()檢查是否會移動或拾取物品,或者如果它們處於戰鬥中或被定位等等。當然,對於玩家來說也是如此,減去一些自動化的東西。

有沒有什麼辦法可以重寫一部分,或者說所有這個模塊,比如C++來獲得更好的性能?目前我們需要的.1秒鐘的滴答時間是在我們現在使用的方法中使用python大約3秒。 (另外,我們已經嘗試了幾種不同的線程類型和堆棧,沒有任何技巧)。

在此先感謝您的幫助!歡迎任何建議!

+3

這真的很健談..你能把它與一些具體問題縮短閱讀嗎? – Levon 2012-07-17 20:41:29

+0

什麼類型是thing.attrs? – casevh 2012-07-17 21:55:29

+3

如果你的東西把他們想做的事情添加到數組中而不是輪詢它們,那會更好。 (即提升事件)。那麼你就不必經歷所有的事情,只有從上次執行以來收到的所有事件的列表。 – Qiau 2012-07-17 22:49:19

回答

3

你的代碼有很多低效率。在剛剛Person.tick方法

展望:

更換

for spell in self.spellTimers: 
      self.spellTimers[spell]['tick'] += 1 

for spell_timer in self.spellTimers.itervalues(): 
      spell_timer['tick'] += 1 

同樣的事情,每個價值至少少兩個字典查找

更換

for thing in Thing.List.values(): 
      if thing: 
       if "person" in thing.attrs: 
        if "spawner" in thing.attrs: 
         thing.tick() 

for thing in Thing.List.itervalues(): 
      if thing and "person" in thing.attrs 
        and "spawner" in thing.attrs: 
        thing.tick() 

(確定一個可能不會快很多,但我更喜歡它)

得到:

from spells import cast,spellmaker 

出蜱()方法和進入文件頂部

替換:

#spell cooldown timer 
self.timers['cooldown'] -= 1 
if self.timers['cooldown'] < 0: 
    self.timers['cooldown'] = 0 

有:

#spell cooldown timer 
self.timers['cooldown'] = max(0, self.timers['cooldown']-1) 

你做一個不少。 你有很多的類似的代碼

#Check if self is doing ranged attack, and then increase timer (ready their weapon) 
    self.timers['ranged'] += 1 
    if self.timers['ranged'] >= int((self.stats['dex']*-1.1)+60+2): 
     self.timers['ranged'] = int((self.stats['dex']*-1.1)+60+1) 
    if self.timers['ranged'] == int((self.stats['dex']*-1.1)+60): 
     self.timers['ranged'] = int((self.stats['dex']*-1.1)+60+1) 

第二if應該elif第一條件爲真,第二個條件永遠是真實的。

記得每次你訪問self.somedict['name']你正在做兩個字典查找(開銷可能會更多,但作爲一個規則使用thum)。如果你在線後有一個dictlookup,你可以通過將代碼分配給一個「臨時」本地變量來加速你的代碼。

如果你不斷瀏覽你的代碼,你會像上面這樣將一個tweek放在一個tweek中,它會把內聯帶入。對不起,我無法完成這一切。

+0

我沒有想到任何人都會像你一樣徹底地經歷它。我只發佈了它,以防有人與方法引用混淆。感謝您指出所有的東西。所以你說的字典查找減慢了很多東西?通過執行'dict = self.somedict [x]',然後使用'dict'來執行該方法中的所有其他操作會更快嗎? – jtsmith1287 2012-07-18 02:54:14

+0

所以,謝謝你!我已經在幾個地方完成了我的代碼,並且我已經看到了性能上的巨大差異。我很高興繼續優化並觀看定時器下降。非常感謝您花時間檢查我的代碼! – jtsmith1287 2012-07-28 19:38:07

1

由於您沒有發佈少量的相關Python代碼行,因此您應該瞭解Python的一件事情是,它可以讓您以相同的Python風格執行時以C風格編寫代碼。很多時候 - 我再也沒有看到過相關的代碼 - 一種比較舊的數據處理方式,而不是像編寫列表理解那樣花費更多的時間,而不是使用更多的Pythonic風格。

通過發佈一個小例子或示例,並詢問是否有更好的方法來加快速度,您可以獲得獲得Pythonic風格的幫助 - 順便說一句,我需要大量的幫助。你也想用你的代碼來捕捉時間。

+2

分析+1。它需要很長時間才能發展出真正讓你放慢腳步的良好直覺。很可能Python本身不是問題。 – 2012-07-17 20:47:52

+0

我現在已經發布了我需要幫助的線路。我忽略了捕獲代碼的時間,以便於閱讀。 – jtsmith1287 2012-07-17 21:39:57

1

用C++或類似的方式重寫它的一部分是可能的,但是如果你的循環需要3秒來處理2000左右的東西,那麼我認爲你很可能會遇到一些嚴重的問題,編碼的東西,而不是Python本身的固有問題。

所以,首先我想第二個分析的建議是在其他答案/它的評論 - 這是真正的最好的方式告訴完全所有的時間正在消失在你的循環。 python available here提供了一些Profiler的一些文檔。其次,如果你使用的是標準的python發行版,那麼如果可能的話,我會考慮遠離線程。 python的默認實現有一個global interpreter lock,它可以防止任何解釋的python代碼真正並行運行,從而消除了許多困擾線程的優勢(並且在某些情況下實際上讓事情變得更慢)

三,我可以就你發佈的代碼提供一些小建議。你仍然最好使用探查器來確認我的懷疑(並找到我沒有注意到的問題區域 - 看起來你有很多代碼),但我會說這可能有助於最少:

在你的for循環for thing in Thing.List.values():你最好打電話Thing.List.itervalues()。這將返回一個遍歷值的迭代器,而不是分配完整的值列表。分配清單是不必要的,除非你計劃在迭代時添加或刪除字典,如果字典中有2000個左右的項目,可能不會太快。這個建議也適用於其他時候迭代代碼中的字典,假設您不打算在迭代時添加/刪除條目(使用.iteritems()而不是.items(),.iterkeys()而不是.keys())。

您可能還想查看與每次迭代調用time.sleep不同的計時方法。有兩個問題我可以看到它 - 主要是time.sleep(.1)實際上並不能保證睡眠會持續1秒,也是因爲它沒有考慮處理所花費的時間長度。例如,假設你的處理時間爲0.05秒,那麼你的睡眠時間爲1秒,然後實際上有兩秒鐘的時間間隔爲15秒 - 而且這種差距只會隨着處理變得更加激烈而增加。我沒有任何具體的建議,但您可能至少想要在將一個數字傳入時考慮處理時間time.sleep

最後,您可能要考慮在某個時間點使用code review stack exchange。我不認爲任何人都會想檢查你的整個遊戲引擎,但它似乎是一個放置代碼塊的好地方,你可能想要反饋,或者你認爲可以改進。

+0

我非常高興能給itervalues()一個嘗試,但立即遇到了一個'RuntimeError'。理由是,這個名單不斷更新。如果生物出現,它會進入列表中。如果玩家登錄,他們會進入列表。我想我需要重新思考如何做蜱蟲。其他遊戲如何做蜱? – jtsmith1287 2012-07-18 02:56:46

+0

@ jtsmith1287恐怕我不確定你會如何做得更好,但是我也許會建議你在使用它的時候不更新列表 - 你可以有一個你建立的「新玩家/小怪物」列表同時運行循環,然後在迭代完成後更新新列表中的主列表。 – obmarg 2012-07-18 10:40:45

+0

絕不會直接在你的列表上迭代,而是在副本上;然後在完成後換一個。 – georgek 2012-07-20 01:55:16

1

你已經有很多很好的具體反饋,所以我只會從泥濘的角度添加一個觀察。有幾個大型泥漿項目使用Python,沒有速度問題(例如Evennia),所以我有信心說你最好重構你所擁有的代碼,而不是下降到C.

+0

我大多數人都同意你的看法,除了我所知道的其他泥土上有2000個怪物正在打勾。我無法邏輯思考如何做到這一點,而無需循環通過某種形式的列表並給出每個表單的勾號。 – jtsmith1287 2012-07-18 15:53:42

+1

@喬歐回答了那個評論OP的問題;一般情況下避免投票支持一系列事件。例如,假設你想每個心跳都打一個怪物,因爲他們是瘋狂的,而且你正在倒數他們的狂暴計時器。相反,當他們狂暴地用(未來時間=遊戲時間+狂暴持續時間)將一個berserk_timer推到隊列中時。每個心跳都會查看該隊列,並且對於每個未來時間等於或超過當前時間的事件,將該事件從列表中彈出並執行。當你擊中將來時間還未來的事件時,停止迭代。 – georgek 2012-07-19 04:11:52

+1

補充一點,如果你想用你的小怪物列表做所有事情,你可以用相同的方式查看黑幫的未來時間。您不必遍歷整個列表。儘管如此,我仍然希望有一系列事件可以反覆使用小怪。 – georgek 2012-07-19 05:24:56