0

所以,首先。这是我的服务器引擎。吉尔穆德.py

好的,这是我以前的小说大小帖子的缩短版本。

上面的链接是我们的 MUD 的 Python 服务器引擎。注意第 73-75 行和第 359 行

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

...

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 个怪物/NPC“生命”所需的可怕方法。tick() 检查他们是否会移动或捡起物品,或者他们是否在战斗中或成为目标等。当然,玩家也一样,减去一些自动化的东西。

有什么方法可以用 C++ 重写这个模块的一部分或全部,以获得更好的性能?目前,我们需要的 0.1 秒滴答声在我们现在使用的方法中使用 python 大约为 3 秒。

(此外,我们尝试了几种不同的线程类型和无堆栈。没有任何效果)。

在此先感谢您的帮助!欢迎任何建议!

4

4 回答 4

3

你的代码有很多效率低下的地方。

只看Person.tick方法:

代替

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

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

同样的事情,每个值至少减少两次 dict 查找

代替

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

跳出 tick() 方法并进入文件顶部

代替:

#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']您都在进行两次 dict 查找(开销可能会更多,但将其用作经验法则​​)。如果你一行一行地有一个dictlookup,你可以通过将它分配给一个“temp”本地变量来加速你的代码。

如果您继续检查您的代码,您将像上述之一一样投入一周时间,这将使事情内联。抱歉,我不能全部看完。

于 2012-07-18T00:01:10.670 回答
1

由于您尚未发布少量相关的 Python 代码行,因此您应该了解有关 Python 的一件事是,它可以让您以 C 风格编写代码,而等效的 Python 风格也可以。很多时候——而且我没有看到你的相关代码——一种较旧的处理数据的方式而不是,例如,编写一个列表理解,会比使用更 Pythonic 的方式花费更多的时间。

您可以通过发布一个或多个小示例并询问是否有更好的方法来加快速度,从而获得 Pythonic 风格的帮助——顺便说一句,我需要很多帮助。您还需要检测您的代码以捕获时间。

于 2012-07-17T20:44:49.687 回答
1

可以用 C++ 或类似代码重写其中的一部分,但如果你的循环需要 3 秒来处理 2000 个左右的项目,那么我认为你的编码方式更有可能遇到一些严重的问题。而不是 python 本身的固有问题。

因此,首先,我想支持其他答案/评论中提出的分析建议-这确实是准确判断循环中所有时间消失的最佳方法。这里有一些关于 python提供的分析器的文档。

其次,如果您使用的是标准的 python 发行版,那么我会考虑尽可能远离线程。python 的默认实现有一个全局解释器锁,可以防止任何解释的 python 代码真正并行运行,从而消除了首先打扰线程的许多优点(并且在某些情况下实际上使事情变慢)

第三,我可以就您发布的代码提供一些小建议。您仍然最好使用分析器来确认我的怀疑(并找到我没有注意到的问题区域 - 看起来您那里有很多代码)但我会说这可能会有所帮助至少:

在你的 for 循环中for thing in Thing.List.values():,你可能会更好地调用Thing.List.itervalues(). 这将返回值的迭代器,而不是分配值的完整列表。分配列表是不必要的,除非您计划在迭代字典时添加或删除字典,并且如果您暗示的字典中有 2000 个左右的项目,可能不会太快。此建议也适用于您在代码中迭代字典的其他时间,假设您不打算在迭代时添加/删除条目(使用.iteritems()代替.items().iterkeys()代替.keys())。

您可能还想研究一种不同于调用time.sleep每次迭代的计时方法。我可以看到两个问题 - 主要是time.sleep(.1)实际上并不能保证睡眠会持续 0.1 秒,还因为它没有考虑处理所需的时间长度。例如,假设您的处理时间为 0.05 秒,然后您睡了 0.1 秒,那么滴答声之间实际上有 0.15 秒的时间 - 这种差距只会随着处理变得更加激烈而增长。我没有任何具体建议,但您可能至少希望在将数字传递到时考虑到处理时间time.sleep

最后,您可能需要考虑在某个时候使用代码审查堆栈交换。我认为没有人会想要审查您的整个游戏引擎,但似乎它可能是放置您可能需要反馈或您认为可以改进的代码块的好地方。

于 2012-07-17T22:18:15.957 回答
1

你已经得到了很多很棒的具体反馈,所以我只会从一个混蛋的角度添加一个观察。有几个大型泥项目使用 Python 而没有速度问题(例如 Evennia),所以我有信心说你最好重构你的代码而不是降到 C。

于 2012-07-18T02:44:54.367 回答