9

私は高性能を必要とするビデオ ゲームに取り組んでいるので、適切なメモリ戦略またはゲームの特定の部分、ゲームの「モデル」である部分、ゲーム表現をセットアップしようとしています。

ゲームのルールに従って、表現の一貫性を保つために内部にさまざまなマネージャーを含む、ゲーム全体の表現を含むオブジェクトがあります。現在、すべてのゲーム エンティティはタイプ固有のファクトリによって生成されているため、これらのエンティティのメモリ管理を必要に応じて分離および変更できるいくつかのファクトリがあります。

現在、私はこれら 2 つの選択肢のどちらかを選択しています。

  1. タイプごとにメモリプールを用意する:オブジェクトプールは割り当てられたオブジェクトのサイズをすでに知っているため、非常に高速な割り当て/割り当て解除と最小限の断片化が可能になります。私を悩ませていることの1つは、そのような複数のプールを別々に持つことです。これにより、他のソリューションがより効率的になる可能性があります...
  2. 1 つのゲーム表現のすべてのファクトリで 1 つの大きなメモリ プールを共有する: (boost::pool のようなものをいくつかのアダプタ関数と共に使用) そうすれば、すべてのゲーム オブジェクト メモリが一緒に割り当てられ、ゲームに 1 ビットの割り当てを持つことができます。合計サイズはすでにわかっています (常にそうとは限りません)。同じプール内に異なるサイズのオブジェクトが存在するため、プール内で断片化が発生する可能性があるため、A よりも優れたソリューションであるかどうかはわかりませんが、メモリ分析やその他の問題の修正にはより簡単なようです。

今、私は A でいくつかの実世界の経験があったので、B の経験がなく、長期的なプロジェクトのために、これらのソリューションに関するアドバイスが欲しいです。長期プロジェクトに適していると思われるソリューションはどれですか? またその理由は? (注: この場合、ゲームモデルがゲーム編集にも使用されるため、プールが本当に必要です。そのため、小さなオブジェクトの割り当て/割り当て解除が大量に発生します)。

明確にするために編集:私はC++ ifを使用しています(まだ明確ではありません)

4

6 に答える 6

10

正解は、問題のドメインに固有です。しかし、私が取り組んでいる問題のあるドメインでは、通常、最初のドメインを選択します。

私はリアルタイムまたはほぼリアルタイムのコードを実行します。主にオーディオの編集と再生。そのコードでは、通常、再生エンジンのヒープからメモリを割り当てる余裕はありません。ほとんどの場合、mallocは十分に速く戻りますが、そうでない場合もあります。そしてそれは時々重要です。

したがって、私たちのソリューションは、特定のオブジェクトに特定のプールを用意し、その他すべてに一般的なプールを使用することです。特定のプールには特定の数の要素が事前に割り当てられており、リンクリスト(実際にはキュー)として実装されているため、割り当てと解放は、ポインターの更新とクリティカルセクションの出入りのコストを超えることはありません。

異常な場合のフォールバックとして。誰かが特別なプールから割り当てる必要があり、それが空の場合-一般的なメモリ(いくつかのオブジェクト)の塊を割り当て、それを特別なプールに追加します。割り当てが特別なプールの一部になると、アプリが終了するか、新しいプロジェクトを開始するまで、割り当てが一般的なプールに戻されることはありません。

特別なプールの初期サイズと最大サイズについて適切な選択を行うことは、アプリケーションを調整する上で重要な部分です。

于 2009-12-27T01:01:39.833 に答える
4

遭遇する問題の 1 つは、STL 実装では、同じ型の 2 つのアロケーターが同等であると想定できることです。これが、Boost.Pool が 1 つのプールのみを使用する理由です (技術的には、タイプごとに異なるプールを使用します)。IE、あなたのアロケーターは、一般的なケースでは非静的メンバーを持つことは許可されていません。ビデオ ゲームを作成していて、STL 実装にこの問題がないことがわかっている場合は、心配する必要はありませlist::splicestd::swap

于 2009-12-27T01:09:40.910 に答える
4

手始めに、あらゆるタイプのビデオゲームに stl または boost を使用することは実用的ではありません。1 つの stl コンテナーを使用した瞬間に、メモリが断片化され、パフォーマンスがトイレで絶望的になり、少なくとも理想と比較されることを絶対に確信できます (ほとんどの人のコードはこのカテゴリに属しているため、ほとんどの人は気付かず、実際に比較することはできません。他に何か)。私は常にそれほど強く考えていたわけではありませんが、時間の経過とともに、数行のコードでさえ小さなグレムリンのようなものであり、最終的にはいつか大きな苦痛を引き起こすのを見てきました.

最初の方法が最も一般的であり、両方を行ったことがある人として、おそらく価値があるよりも多くの時間とエネルギーを問題に費やしたくない場合は、おそらくこれが唯一の実用的な方法です。2 番目の方法は、より一般的でありながら、正確なニーズに合わせて調整できるため、優れていますが、多くの作業が必要であり、簡単に実行できるものではありません。

于 2009-12-27T10:05:38.953 に答える
2

考えられる解決策の 1 つは、1. と 2. の間の何かです。

小さなオブジェクトにはプールを使用します。オブジェクト サイズごとに 1 つのプールを使用します。この場合、ポインターを配列に格納することでプールを簡単に見つけることができます。

さらに、大きなオブジェクト用に 1 つのプールを持つことができます。この場合、断片化の可能性は低く、大きなオブジェクトが頻繁に割り当てられたり割り当て解除されたりしないため、時間のオーバーヘッドはそれほど重要ではありません。

について注意してくださいboost::pool。のパフォーマンスをテストするときはboost::pool、割り当てだけでなく解放もテストします。私はそれを経験しましたがboost::poolboost::fast_pool解放時間は非常に長くなる可能性があります。私のケースは、1 つのプール内の異なるサイズの小さなオブジェクトの割り当てと割り当て解除で構成されていました

于 2009-12-27T00:32:35.667 に答える
0

あなたが検討しているメモリ マネージャーについて、私は具体的な経験はありませんが、役に立つかもしれない一般的なガイドラインを以下に示します。

  • メモリ不足が予想されない場合は、オプション 1 が最適です。なぜなら、あなたが述べているように高速であり (2 よりも高速ですか?)、個別のプールを使用すると、割り当て/割り当て解除/バッファの問題を見つけやすくなります (プール マネージャーが適切なエラー検出機能を備えています)。
  • メモリが問題になる可能性がある場合 (ゲームでは、ターゲット プラットフォームで一般的に利用可能なメモリと比較して多くのメモリが必要になる場合など)、1 つの大きなプールを使用すると、メモリをより効率的に使用できます。また、プールごとの平均メモリ要件と最大メモリ要件を正確に予測できない場合は、メモリ マネージャーがメモリ プールを動的に拡張できる (理想的にはプールからブロックを動的に解放する) ことができない限り、これを選択することをお勧めします。私が見た 2 の唯一の欠点は、遅くなる可能性があること (そうですか?) と、メモリ管理のエラーを検出するのが難しくなる可能性があることです。

複数のプールを使用して開発し、最終テストと本番リリースを 1 つのプールで行うことで、両方の長所を活かすことができます (速度は同程度であると仮定)。そうすれば、開発中に割り当て/管理の問題を見つけることができますが、潜在的により効率的な単一プールの恩恵を受けることができます.

于 2009-12-27T00:27:21.890 に答える
0

実際には、2 を使用します。Linux カーネルの例を挙げます。カーネルでは、ユーザーへの応答性を向上させるために、dentry (ディレクトリ エントリ) と inode オブジェクトをメモリに長時間キャッシュする必要があります。inode オブジェクトはファイル システムに依存するため、各ファイル システムは独自のオブジェクト プールを作成します。オブジェクトが類似している場合にできるもう 1 つの方法は、オブジェクトを抽象化し、共通の属性を 1 つの抽象オブジェクトに保持し、コンテナーを使用してオブジェクト固有の情報を格納することです。完全なアイデアについては、以下のコードを参照してください。

http://lxr.linux.no/linux+v2.6.32/fs/ext2/super.c#L149

于 2009-12-27T03:31:30.327 に答える