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()要するに、4500msの値に増加するループを引き起こしている関数です。

更新が呼び出されるたびに、マップ オブジェクトは、新しいオブジェクトがストアに追加されているかどうかを確認する必要があります。オブジェクトが追加された場合、そのオブジェクトは、追加されたことを他のすべてのオブジェクトに通知する必要があります。

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の には互いに見えるユニットのみが含まれるため、更新が必要になります。オブジェクト数が少ない限り機能します...

他に何ができますか?ありがとうございました!



ノート

これらはすべてfrom toforeachのようなイテレータを使用したループですrde::hash_map<...>::iteratorobjects.begin()objects.end()

プレイヤー (NPC ではなく実際のユーザー) がいない場合は更新しない、特定のマップにプレイヤーがいない場合はメモリを解放するなど、その他の最適化は既に考慮されています。

4

1 に答える 1

1

空間セグメンテーション以外に頭に浮かぶ最初の最適化は、オブジェクトがそれらの近くの変更 (四分木など) についてのみ通知されるようにするためのものです。すべてのオブジェクトにすべての変更について通知する必要がありますか? update各オブジェクトに、「すべてのフレームでメソッドを実行します」と伝えてみませんか。メソッドでは、update必要なものすべてを探して (たとえば、このフレーム/最近のフレームで発生したすべての変更のバッファーがある可能性があります)、必要に応じて自分自身を更新できます。こうすることで、オブジェクトが実際に知る必要や気にする必要のないことをオブジェクトに通知するために CPU サイクルを費やす必要がなくなります。

また、プログラムで CPU プロファイラーを実行し、ホット スポット (最も多くの CPU 時間が費やされている場所) が想定されている場所にあることを確認しましたか?

詳細については、コメントを参照してください。

 |
 |
\|/
 V
于 2013-06-04T23:52:13.730 に答える