0

私は、割り当てられたオブジェクトを取得し、後でその割り当てをリサイクルするプーリングアルゴリズムに取り組んでいます。ただし、オブジェクトのリサイクルに関する問題の1つは、たとえば次のとおりです。

 someObject obj = pool.alloc(); //gives me a new object if no previous allocations, If an allocation has been recycled, returns a previous allocation
 obj.someVariable = "foo";
 pool.recycle(obj);

上記のコードは既存の割り当てを取得して保存するため、別のsomeObjectがある場合に備えて余分なRAMを割り当てる必要はありません。ただし、次の場合に問題が発生します。

 someObject obj = pool.alloc(); //gives me the above allocation
 obj.otherVariable = "bar";
 obj.dump();

その結果、次の結果が得られます。

  someVariable = foo
  otherVariable = bar

上記のアプローチは問題を引き起こします。何らかの理由で(または他の誰かが)オブジェクト内で特定の変数を使用しないアルゴリズムを使用している場合、古い値によって不要な動作が発生する可能性があります。デフォルトのコンストラクターを再度呼び出す方法があるかどうかを確認するために少し調べましたが(悪い考え)、C#では(ありがたいことに)そうすることができないようです。しかし、これを行うためにリフレクションを使用する方法があるかどうか疑問に思いましたか?また、オブジェクト内の変数をクリアすると、malloc(新規)を回避する目的が無効になりますか?言い換えると、変数のクリアに時間を費やすと、パフォーマンスの向上は最小限になりますか?私は自分自身にプールを教えようとしているので、どんな批判やアドバイスも大歓迎です!

4

1 に答える 1

1

プーリングを見る 1 つの方法を次に示します。

プーリングが本当に必要になるのは、既存のオブジェクトを取得することで回避できる複雑な設定作業を再利用する場合だけです。たとえば、オブジェクト プーリングの最もよく知られているアプリケーションの 1 つは、「データベース接続」の形式です。

プーリングはデータベース接続 (DBConns) に対して機能します。1) DBConn は接続文字列によって自明に識別できるためです。2) DBConn の確立には多くの作業と時間がかかります。単純な識別は、接続文字列を照合することによって行われます。2 つの接続文字列が同一である場合、それらが確立する接続も同一である必要があります。また、接続文字列を取得すると、サーバー アドレスの検索、ソケットのオープン、認証、および接続の確立に数百ミリ秒かかる場合があります。これは、接続が解放されると、次に同じ接続文字列に対して接続が要求されたときに再利用できるため、プーリングがデータベース接続に対して適切に機能することを意味します。

.NET ランタイム環境は、オブジェクトの迅速な割り当てと解放に非常に優れているため、メモリの問題は発生しません。メモリの使用や割り当ての速度だけが心配な場合は、気にしないでください。自分でメモリをゼロにしてコンパイラのパフォーマンスを打ち負かすことはできません。ただし、プールから既存のオブジェクトを取得することで回避できる複雑で時間のかかるセットアップがオブジェクトにある場合は、いくつかの利点があります。

プーリングのもう 1 つの良い例は、ビデオゲームのパーティクル システムです。何百ものパーティクルを作成し、ライフサイクルを通過し、破棄する必要がありますが、古いパーティクルが消滅した後に新しいパーティクルを作成する必要があります。典型的なパーティクル システムは、X 個のオブジェクトを配列として作成し、死んだオブジェクトを元の場所に新しく作成されたパーティクルとして生き返らせる Reset() 関数を備えています。これが機能する理由は、パーティクルを簡単に識別でき、セットアップ作業 (グラフィックス システムにテクスチャを与える、場所を配置するなど) ができるためです。

あなたのアプリケーションには、「単純な一致識別」機能と、回避できる長いセットアップ プロセスの両方がありますか? そうでない場合は、毎回新しいオブジェクトを割り当てるだけです。パフォーマンスの低下は見られないでしょう。

編集:パフォーマンスの観点から、ここでこれを見てみましょう:

変数のリセット = O(N) 割り当てステートメント。リフレクションを使用すると、それが大幅に増加する可能性があります

新しいオブジェクトをインスタンス化する = 1 つの malloc 呼び出し、1 つのコンストラクター呼び出し。複雑さのレベルは、コンストラクター コードとメモリの断片化に依存します。

リフレクションを使用して変数をリセットすることはできますが、代入の O(N) は malloc やコンストラクターよりも高速であることを事前に知っておく必要があります。データベース接続の場合、条件が満たされていることがわかっています。しかし、それはあなたのプールに当てはまりますか?

編集:以下のコメントから、プーリングが適切なケースが実際に見つかった可能性があります。その場合、理想的なアプローチは、Reset()プールしているクラスの関数を作成することです。IPoolable関数を定義するインターフェイスを作成してみてくださいReset()。次に、プールするクラスごとに、Reset()関数を定義して、すべてのキー変数をゼロに設定します。コンパイルされるため、リフレクション オーバーヘッドが発生せず、動的コードでは不可能なオブジェクト固有の最適化を維持できます。

プールの場合、プール クラスを次のように定義しますMyPool<IPoolable>。その後、以前にリサイクルされたオブジェクトが取得されるたびReset() に、呼び出し元に戻す前にそれを呼び出すことができます。

于 2012-10-08T18:50:44.810 に答える