1

介绍

首先,我必须说这是为在线游戏服务器完成的,它跟踪地图中的每个对象,无论是玩家还是 AI(NPC 或您想称呼它)。

它不仅必须跟踪他们,还必须在玩家之间通知他们附近的玩家。我已经解决了这个问题,我目前正在使用多线程方法,它非常有效。

我的问题

我将所有对象存储在哈希表中。我们可以认为哈希表是一个unordered_map,虽然我实际上正在使用rde::hash_map,因为它在插入和获取(自测)时更快,虽然需要更多的 RAM(现在不是问题)。

问题是,该映射存储对象的唯一 ID(64 位)以及对象指针,例如: rde::hash_map<UInt64, Object*>

我的问题是:

我的应用程序(服务器)必须在一个循环中运行(在一个线程内),该循环必须每 50 毫秒调用一次,以保持运行顺利。循环代码如下所示:

void loop()
{
    UInt32 prev = clock();
    UInt32 prevSleep = 0;
    while (1)
    {
        UInt32 diff = clock() - prev;
        prev = clock();

        maps.update() // Suppose map is a class, which stores the objects map

        if (diff <= 50 + prevSleep)
        {
            prevSleep = 50 + prevSleep - diff;
            sleep(prevSleep);
        }
        else
            prevSleep = 0;
    }
}

现在,到了这一点,map::update()导致循环增加到 4500 毫秒值的函数。

每次调用更新时,地图对象必须检查是否有新对象添加到存储中,如果添加了一个对象,该对象必须通知所有其他对象它正在添加,我通过(伪代码)执行此操作:

foreach obectsToBeAdded as joiner:
    foreach objectsList as object:
        joiner->notify(object);
        object->notify(joiner);

稍后,必须调用每个对象的内部更新,我这样做(再次伪代码):

foreach objectsList as object:
    object->update();

而且,如果这还不够,则必须将上述循环扩展为:

foreach objectsList as object:
    object->update()
    
    // Visit all the other objects
    // Called once every 1 sec for the object, not on every call
    foreach objectsList as other:
        if other != object:
            object->visit(other)

我尝试优化这个

将第一个循环(添加和通知)与更新循环合并,如下所示:

foreach objectsList as object:
    foreach objectsToBeAdded as joiner:
        object->notify(joiner)
        joiner->notify(object)

    object->update()

    // Called once every 1 sec for the object, not on every call
    foreach objectsList as other:
        if other != object
            object->visit(other)

这在对象数量不大的情况下有效,只要它增加循环开始最多需要 4 秒,这远远超出了我正在寻找的 50 毫秒。

我的问题

有没有其他方法可以进一步优化?我虽然使用八叉树来跟踪地图中的对象位置,但后来得出的结论是它只会使问题恶化。

我还将每个地图划分为实体的 35 个单元(35 是“视图范围”,LOS),因此给定的rde::hash_map仅包含彼此可以看到的单元,因此需要更新。只要对象计数低就可以工作......

我还能做什么?谢谢!



笔记

所有这些foreach都是使用迭代器的循环,比如rde::hash_map<...>::iteratorfrom objects.begin()toobjects.end()

其他优化,例如在没有玩家(真实用户,而不是 NPC)时不更新,在给定地图上没有玩家时释放内存等等,已经被考虑在内。

4

1 回答 1

1

除了空间分割之外,想到的第一个优化是让对象只知道它们附近的变化(例如使用四叉树):每个对象都必须被告知每一个变化吗?为什么不告诉每个对象,'每一帧,我都会运行你的update方法。在您的update方法中,您可以查找您想要的所有内容(例如,可能存在此帧/最近帧中发生的所有更改的缓冲区)并根据需要更新自己。这样,您就不会花费 CPU 周期来通知对象他们实际上不需要知道或关心的事情。

此外,您是否在您的程序上运行了 CPU 分析器,并验证了热点(花费最多 CPU 时间的地方)是否在您认为的位置?

请参阅评论以进行进一步讨论:

 |
 |
\|/
 V
于 2013-06-04T23:52:13.730 回答