ホームページ  >  記事  >  バックエンド開発  >  高パフォーマンスの .NET を作成するための実践的なチュートリアル

高パフォーマンスの .NET を作成するための実践的なチュートリアル

零下一度
零下一度オリジナル
2017-06-25 09:08:431466ブラウズ

割り当て率の削減

これについてはほとんど説明する必要はありません。メモリ使用量が削減され、GC リサイクル時の負荷が自然に軽減され、同時にメモリの断片化と CPU 使用率も削減されます。これを実現するにはいくつかの方法を使用できますが、他の設計と競合する可能性があります。

デザインする際には、各オブジェクトを慎重に調べて自問する必要があります:

  1. このオブジェクトは本当に必要ですか?

  2. このフィールドは必要ですか?

  3. 配列のサイズを減らすことはできますか?

  4. プリミティブのサイズを減らすことはできますか (Int64 を Int32 に置き換えるなど)。

  5. これらのオブジェクトはまれな場合にのみ使用されるのでしょうか、それとも初期化中にのみ使用されるのでしょうか?

  6. 一部のクラスを構造体に変換して、スタックに割り当てたり、オブジェクトの一部にしたりすることは可能ですか?

  7. 大量のメモリを割り当てているのに、そのほんの一部しか使用していませんか?

  8. 関連データを他の場所から入手できますか?

小話: サーバー側でリクエストに応答する関数で、メモリセグメントよりも大きなメモリがリクエストで割り当てられることがわかりました。これにより、CLR ではすべての世代 0 オブジェクトが 1 つのメモリ セグメントに存在する必要があり、現在割り当てられているメモリ セグメントがいっぱいの場合は、新しいメモリ セグメントが開かれ、元のメモリが開かれるためです。セグメントがオープンされます。メモリセグメントは 2 世代にわたってリサイクルされます。メモリ割り当てを減らす以外に方法がないため、これは良い実装とは言えません。

最も重要なルール

ガベージ コレクションを使用した高パフォーマンス プログラミングには基本的なルールがあり、これは実際にはコード設計の指針となります。

世代 0 でオブジェクトを収集するか、まったく収集しません。
違いは、GC 中にオブジェクトのライフサイクルを非常に短くすることです。決して触れないでください。それができない場合は、削除する必要があります。できるだけ早く第 2 世代に移行し、永久にそこに留まり、リサイクルされることはありません。これは、存続期間の長いオブジェクトへの参照を永久に保持することを意味します。通常、これはオブジェクト、特に大きなオブジェクト ヒープ内のオブジェクトを再利用できることも意味します。 上位世代ごとの GC リサイクルには、前の世代よりも時間がかかります。多くの世代 0、1 オブジェクトと少数の世代 2 オブジェクトを保持したい場合。 2 世代のリサイクルを行うためにバックグラウンド GC がオンになっている場合でも、大量の CPU 操作が消費されます。GC の代わりに CPU のこの部分をアプリケーションに使用することをお勧めします。


注: 10 世代の 0 リサイクルごとに 1 世代のリサイクルが生成され、10 世代の 1 リサイクルごとに 2 世代のリサイクルが生成されるという格言を聞いたことがあるかもしれません。これは実際には誤りですが、できるだけ多くの高速な世代 0 コレクションを生成し、少数の世代 2 コレクションを生成する必要があることを理解する必要があります。

ジェネレーション 1 のリサイクルは避けた方がよいでしょう。これは主に、ジェネレーション 0 からジェネレーション 1 に昇格されたオブジェクトがこの時点でジェネレーション 2 に転送されるためです。世代 1 は、世代 2 に入るオブジェクトのバッファーです。

理想的には、割り当てたすべてのオブジェクトは、次の世代 0 コレクションの前にライフサイクルを終了する必要があります。 GC 間の時間を測定し、アプリケーション内のオブジェクトの存続期間と比較できます。ツールを使用してライフサイクルを測定する方法については、この章の最後に記載されています。
このように考えることに慣れていないかもしれませんが、このルールはアプリケーションのあらゆる側面に切り込まれており、この最も重要なルールを実行できるように、頻繁に考えて考え方を根本的に変える必要があります。


オブジェクトのライフサイクルを短縮します

オブジェクトのスコープが短いほど、次の GC が発生したときにオブジェクトが次の世代に昇格される可能性が低くなります。一般に、オブジェクトは必要になるまで作成しないでください。

同時に、オブジェクト作成のコストが非常に高い場合は、他の処理ロジックを妨げないように、例外を早期に作成できます。

さらに、オブジェクトができるだけ早くスコープから出るようにする必要もあります。ローカル変数の場合、最後の使用後、またはメソッドが終了する前でも、その有効期間を終了できます。コードを {} で囲むこともできます。実行には影響しませんが、コンパイラは、このスコープ内のオブジェクトがライフサイクルを完了し、使用されなくなったとみなします。オブジェクトのメソッドを呼び出す必要がある場合は、GC ができるだけ早くオブジェクトをリサイクルできるように、最初と最後の呼び出しの間の時間を短縮するようにしてください。

長期間維持されるオブジェクトと関連付け(参照)されている場合は、参照関係を解除する必要があります。 Null チェックが増える可能性があり、コードがより複雑になる可能性があります。また、特にデバッグ時に、オブジェクトの利用可能な状態 (常に完全な状態が利用可能) と効率の間に緊張が生じる可能性があります。
1 つの解決策は、クリアするオブジェクトをログ メッセージなどの別の方法に変換して、その後のデバッグ中に関連情報をクエリできるようにすることです。
もう 1 つの方法は、(オブジェクト間の関係を解放せずに) コードに構成可能なオプションを追加することです。プログラムを実行します (または、特定のリクエストなど、プログラムの特定の部分を実行します)。このモードでは、オブジェクト参照は解放されません。ただし、デバッグにできるだけ便利なようにオブジェクトを維持するためです。

オブジェクト階層の深さを減らす

この章の冒頭で述べたように、GC はリサイクル時にオブジェクトの参照関係に沿ってトラバースします。サーバー GC モードでは、GC はマルチスレッド方式で実行されますが、スレッドが深いレベルでオブジェクトを処理する必要がある場合、そのスレッドを処理したすべてのスレッドは、終了する前にこのスレッドの処理が完了するまで待機する必要があります。 。将来の CLR バージョンでは、この問題にあまり注意を払う必要はありません。GC は、マルチスレッド実行時の負荷分散に、より優れたマーキング アルゴリズムを使用します。ただし、オブジェクト レベルが非常に深い場合でも、この問題に注意する必要があります。

オブジェクト間の参照を減らす

これは前のセクションの深さに関連しますが、他の要因もいくつかあります。
オブジェクトが多くのオブジェクト (配列、リストなど) を参照する場合、オブジェクトの走査に多くの時間がかかります。 GC は複雑な関係グラフを持つため、長い間問題を引き起こしています。
もう 1 つの問題は、オブジェクトが持つ参照関係の数を簡単に判断できない場合、オブジェクトのライフ サイクルを正確に予測できないことです。この複雑さを軽減することは、コードをより堅牢にするだけでなく、デバッグを容易にしてパフォーマンスを向上させるためにも非常に必要です。
さらに、異なる世代のオブジェクト間の参照、特に古いオブジェクトから新しいオブジェクトへの参照も GC の非効率を引​​き起こすことに注意してください。たとえば、第 2 世代のオブジェクトが第 0 世代のオブジェクトに参照関係がある場合、第 0 世代の GC が発生するたびに、第 2 世代のオブジェクトの一部もスキャンして、第 0 世代のオブジェクトへの参照がまだ保持されているかどうかを確認する必要があります。 。これは完全な GC ではありませんが、それでも不必要な作業であるため、この状況を回避するように努める必要があります。

オブジェクトの固定を避ける (固定)

オブジェクトを固定すると、マネージド コードからローカル コードに転送されるデータの安全性を確保できます。一般的なものは配列と文字列です。コードがネイティブ コードと対話する必要がない場合は、パフォーマンスのオーバーヘッドを心配する必要はありません。
オブジェクトを固定すると、ガベージ コレクション (圧縮フェーズ) 中にオブジェクトを移動できなくなります。オブジェクトの固定によって大きなオーバーヘッドは発生しませんが、GC リサイクル操作が妨げられ、メモリの断片化が発生する可能性が高くなります。 GC は、再割り当て時にオブジェクト間のスペースを利用できるように、リサイクル中にオブジェクトの位置を記録します。ただし、固定されたオブジェクトが多数ある場合、メモリの断片化が増加します。
ペグは明示的または暗黙的にすることができます。ここに示されているのは、GCHandle を使用する場合は GCHandleType.Pinned パラメーターで設定するか、アンセーフ モードで fix キーワードを使用することです。 fix キーワードを使用する場合と GCHandle を使用する場合の違いは、Dispose メソッドが明示的に呼び出されるかどうかです。 fix の使用は非常に便利ですが、非同期状況では使用できません。ただし、ハンドル オブジェクト (GCHandle) を作成し、それを返してコールバック中に処理することはできます。
暗黙的な固定オブジェクトはより一般的ですが、検出して削除するのも難しくなります。最もわかりやすい例は、プラットフォーム呼び出し (P/Invoke) を通じてアンマネージ コードにオブジェクトを渡すことです。これはコードだけではありません。頻繁に呼び出すマネージド API の一部は、実際にネイティブ コードを呼び出したり、オブジェクトをピン留めしたりすることもあります。
CLR は独自のデータの一部も固定しますが、通常は気にする必要はありません。
理想的には、オブジェクトをできるだけ固定しないようにする必要があります。それが不可能な場合は、前述の重要なルールに従い、これらの固定されたオブジェクトをできるだけ早く解放するようにしてください。オブジェクトが固定されて解放されるだけの場合、収集操作に影響を与える可能性はほとんどありません。また、複数のオブジェクトを同時に固定することも避けたいです。ピン留めされたオブジェクトが世代 2 にスワップされるか、LOH に割り当てられると、若干改善されます。このルールに従って、ラージ オブジェクト ヒープに大きなバッファを割り当て、実際のニーズに応じてバッファを自分で管理できます。または、小さいオブジェクトのペアにバッファを割り当ててから、固定する前にそれらを世代 2 にアップグレードします。これは、オブジェクトを世代 0 に直接固定するよりも優れています。

以上が高パフォーマンスの .NET を作成するための実践的なチュートリアルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。