2

Java では、各オブジェクトに同期モニターがあります。したがって、実装はメモリ使用量の点でかなり凝縮されており、うまくいけば高速でもあると思います。

これを C++ に移植する場合、最適な実装は何でしょうか。「pthread_mutex_init」よりも優れたものがあるに違いないと思いますか、それともJavaのオブジェクトオーバーヘッドは本当に高いですか?

編集: Linux i386 の pthread_mutex_t が 24 バイトであることを確認しました。オブジェクトごとにこのスペースを確保しなければならない場合、それは膨大です。

4

4 に答える 4

3

ある意味ではpthread_mutex_init、実際よりも悪いです。Java の待機/通知のため、モニターを実装するには、mutex と条件変数のペアが必要です。

実際には、JVM を実装するときは、本にあるすべてのプラットフォーム固有の最適化を探し出して適用し、いくつかの新しい最適化を発明して、モニターを可能な限り高速にします。本当に恐ろしい仕事をすることができないなら、あなたはガベージコレクションを最適化することができません;-)

1 つの観察結果は、すべてのオブジェクトが独自のモニターを持つ必要があるわけではないということです。現在同期されていないオブジェクトは必要ありません。そのため、JVM はモニターのプールを作成することができ、各オブジェクトはポインター フィールドを持つことができます。これは、スレッドが実際にオブジェクトを同期したいときに (たとえば、プラットフォーム固有のアトミックな比較およびスワップ操作を使用して) 埋められます。したがって、モニターの初期化のコストは、オブジェクト作成のコストに追加する必要はありません。メモリが事前にクリアされていると仮定すると、オブジェクトの作成は次のようになります。ポインターをデクリメントします (さらに、ある種の境界チェック、gc を実行するコードへの予測偽分岐など)。タイプを記入してください。最も派生したコンストラクターを呼び出します。Object のコンストラクターが何もしないように調整できると思いますが、明らかに多くは実装に依存します。

実際には、平均的な Java アプリケーションは一度に非常に多くのオブジェクトを同期するわけではないため、モニター プールは時間とメモリの大幅な最適化になる可能性があります。

于 2009-11-18T00:57:21.390 に答える
2

Sun Hotspot JVM は、compare と swapを使用してシンロックを実装します。オブジェクトがロックされている場合、待機中のスレッドは、オブジェクトをロックしたスレッドのモニターで待機します。これは、スレッドごとに重いロックが 1 つだけ必要であることを意味します。

于 2009-11-18T00:58:59.797 に答える
2

Javaがどのようにそれを行うのかはわかりませんが、.NETはミューテックス(またはアナログ-それを保持する構造は「syncblk」と呼ばれます)をオブジェクトに直接保持しません。むしろ、それには syncblk のグローバル テーブルがあり、オブジェクトはそのテーブルのインデックスによってその syncblk を参照します。さらに、オブジェクトが作成されるとすぐに syncblk を取得するのではなく、最初のロック時に必要に応じて作成されます。

私は、オブジェクトとその syncblk をスレッドセーフな方法で関連付けるために、アトミックな比較交換を使用していると思います (注意してください、実際にそれがどのように行われるかはわかりません!)。

  1. オブジェクトの隠しsyncblk_indexフィールドが 0 であることを確認します。0 でない場合は、ロックして続行します。それ以外の場合は...
  2. グローバル テーブルに新しい syncblk を作成し、そのインデックスを取得します (グローバル ロックは、必要に応じてここで取得/解放されます)。
  3. オブジェクト自体に書き込むための比較交換。
  4. 以前の値が 0 だった場合 (0 は有効なインデックスではなく、syncblk_indexオブジェクトの非表示フィールドの初期値であると仮定します)、syncblk の作成は争われませんでした。ロックオンして進みます。
  5. 以前の値が 0 でない場合は、他の誰かが既に syncblk を作成しており、私たちが作成している間にそれをオブジェクトに関連付けており、現在その syncblk のインデックスを取得しています。作成したばかりのものを破棄し、取得したものをロックします。

したがって、オブジェクトごとのオーバーヘッドは、最良の場合で 4 バイト (syncblk テーブルへの 32 ビット インデックスを想定) ですが、実際にロックされているオブジェクトの場合はより大きくなります。オブジェクトをめったにロックしない場合、このスキームはリソースの使用を削減する良い方法のように見えます。ただし、最終的にほとんどまたはすべてのオブジェクトをロックする必要がある場合は、ミューテックスをオブジェクト内に直接格納する方が高速な場合があります。

于 2009-11-18T01:06:12.573 に答える
1

確かに、すべてのオブジェクトにそのようなモニターは必要ありません!

Java から C++ に移植するとき、すべてをやみくもにコピーするのは悪い考えだと思います。Java に最適な構造は、C++ に最適な構造と同じではありません。Java にはガベージ コレクションがあり、C++ にはないためです。

本当に必要なオブジェクトだけにモニターを追加します。型の一部のインスタンスのみが同期を必要とする場合、同期に必要なミューテックス (および場合によっては条件変数) を含むラッパー クラスを作成することはそれほど難しくありません。他の人がすでに言ったように、別の方法は、オブジェクトアドレスのハッシュを使用して配列にインデックスを付けるなど、オブジェクトごとに1つを選択する何らかの手段で同期オブジェクトのプールを使用することです。

各ターンでプラットフォームの仕様に依存するのではなく、移植性のためにブースト スレッド ライブラリまたは新しい C++0x 標準スレッド ライブラリを使用します。Boost.Threadは、Linux、MacOSX、win32、Solaris、HP-UX などをサポートしています。私の C++0x スレッド ライブラリの実装は現在、Windows と Linux のみをサポートしていますが、他の実装もいずれ利用可能になる予定です。

于 2009-11-18T11:04:21.003 に答える