序章
まず、これはオンライン ゲーム サーバー用に行われていると言わざるを得ません。このサーバーは、プレイヤーであろうと 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<...>::iterator
objects.begin()
objects.end()
プレイヤー (NPC ではなく実際のユーザー) がいない場合は更新しない、特定のマップにプレイヤーがいない場合はメモリを解放するなど、その他の最適化は既に考慮されています。