C++ での Java のようなオブジェクト モニターの最も効率的な実装は何ですか?
-
20-09-2019 - |
質問
Java では、各オブジェクトに同期モニターがあります。したがって、実装はメモリ使用量の点でかなり凝縮されており、高速であることが期待されます。
これを C++ に移植する場合、最適な実装は何でしょうか。「pthread_mutex_init」よりも優れたものがあるに違いないと思います。それとも、Java のオブジェクトのオーバーヘッドは本当に高いのでしょうか?
編集:Linux i386 の pthread_mutex_t が 24 バイトの大きさであることを確認しました。このスペースをオブジェクトごとに予約しなければならないとしたら、それは膨大な量になります。
解決
日ホットスポットJVMの実装の比較のを使用して、の薄いのロックそして、スワップをに。オブジェクトがロックされている場合、待機中のスレッドがオブジェクトをロックしたスレッドのモニタに待ちます。これは、あなただけのスレッドごとに一つの重ロックを必要とすることを意味します。
他のヒント
は、ある意味では、それは実際には、pthread_mutex_init
よりも悪いです。そのため、Javaの待機/通知のあなたは、種類のモニターを実装するためにペアミューテックスと条件変数を必要とします。
実際には、あなたは追い詰めると本の中ですべての単一のプラットフォーム固有の最適化を適用し、できるだけ速くモニターを作るために、いくつかの新しいものを発明するJVMを実装する場合。あなたがそれを本当に悪魔のような仕事をすることができない場合、あなたは間違いなく、ガベージコレクションを最適化するまではありません; - )
一つの観察はないすべてのオブジェクトは、独自のモニターを持っている必要があるということです。現在同期されていないオブジェクトは、1つを必要としません。そうJVMは、モニターのプールを作成することができ、各オブジェクトは、単に(例えば、プラットフォーム固有のアトミック比較およびスワップ操作で)スレッドは実際にオブジェクトに同期させたいときに充填されたポインタフィールドを有することができます。だから、モニターの初期化のコストは、オブジェクト作成のコストに追加する必要はありません。 、オブジェクトの作成が可能なメモリを事前にクリアされると仮定すると:(ようにGCを実行し、コードの予測 - 偽の枝で、プラス境界のいくつかの種類のチェック)ポインタをデクリメント。タイプを記入。ほとんどの派生コンストラクタを呼び出します。私はあなたが何もしないように、オブジェクトのコンストラクタを手配することができると思いますが、明らかに多くの実装に依存します。
実際には、平均的なJavaアプリケーションをモニタープールが潜在的に時間とメモリの大幅な最適化されているので、一度に非常に多くのオブジェクトを上の同期化されていません。
Java がどのように行うのかはわかりませんが、.NET はミューテックス (またはアナログ - ミューテックスを保持する構造は「syncblk」と呼ばれます) をオブジェクト内に直接保持しません。むしろ、syncblk のグローバル テーブルがあり、オブジェクトはそのテーブル内のインデックスによって syncblk を参照します。さらに、オブジェクトは作成されてすぐに syncblk を取得するのではなく、最初のロック時にオンデマンドで作成されます。
私は、アトミックな比較交換を使用してオブジェクトとその syncblk をスレッドセーフな方法で関連付けていると仮定します (注、実際にどのように行うのかはわかりません)。
- 隠れているものを確認する
syncblk_index
オブジェクトのフィールドが 0 の場合。0 でない場合はロックして続行します。そうでない場合は... - グローバル テーブルに新しい syncblk を作成し、そのインデックスを取得します (グローバル ロックは必要に応じてここで取得/解放されます)。
- 比較交換してオブジェクト自体に書き込みます。
- 以前の値が 0 だった場合 (0 は有効なインデックスではなく、非表示のインデックスの初期値であると仮定します)
syncblk_index
オブジェクトのフィールド)、syncblk の作成には異議が唱えられませんでした。ロックして先に進みます。 - 以前の値が 0 でない場合は、私たちが作成している間に他の誰かがすでに syncblk を作成し、それをオブジェクトに関連付けており、現在その syncblk のインデックスを持っています。作成したばかりのものを破棄し、取得したものをロックオンします。
したがって、オブジェクトごとのオーバーヘッドは最良の場合で 4 バイト (syncblk テーブルへの 32 ビットのインデックスを想定) ですが、実際にロックされているオブジェクトの場合はさらに大きくなります。オブジェクトをロックオンすることがほとんどない場合、このスキームはリソースの使用量を削減する良い方法のように見えます。ただし、最終的にほとんどまたはすべてのオブジェクトをロックする必要がある場合は、ミューテックスをオブジェクト内に直接保存した方が高速になる可能性があります。
確かにあなたのために、このようなモニターを必要としないのすべてののオブジェクト!
JavaからC ++に移植する場合、、それだけで盲目的にすべてをコピーするための悪いアイデアとして私を打ちます。 Javaはガベージコレクションを持っており、C ++にはないので、Javaのための最良の構造はC ++ではなく、少なくともための最良のと同じではありません。
実際にそれを必要とするオブジェクトのみにモニターを追加します。タイプの唯一のいくつかの例では、同期が必要な場合、それは同期のために必要なミューテックス(そしておそらく条件変数)を含んラッパークラスを作成することは難しいことではありません。他の人が既に述べたように、代替的には、そのようなインデックスにアレイを対象アドレスのハッシュを使用するなど、各オブジェクトのいずれかを選択するいくつかの手段を用いて同期オブジェクトのプールを使用することである。
私はむしろ、各ターンでのプラットフォームの仕様に依存するよりも、ブーストスレッドライブラリや移植のための新しいC ++ 0xの標準スレッドライブラリを使用すると思います。 Boost.Thread のLinuxでは、MacOSXは、Win32の、Solaris版、HP-UXなどをサポートしています。マイのC ++ 0xのスレッドライブラリのhref="http://www.stdthread.co.uk" rel="nofollow noreferrer">実装やがて利用できます。