154

allocate()またはにallocateDirect()、それが問題です。

DirectByteBufferここ数年、 s は OS レベルでの直接メモリ マッピングであるため、get/put 呼び出しの方が s よりも高速に実行されるという考えに固執してきましたHeapByteBuffer。今まで、状況に関する正確な詳細を知ることにあまり興味がありませんでした。ByteBuffer2 種類のs のどちらが速く、どのような条件であるかを知りたいです。

4

4 に答える 4

158

Ron Hitches は、彼の優れた著書Java NIOで、あなたの質問に対する適切な回答と思われるものを提供しているようです。

オペレーティング システムは、メモリ領域で I/O 操作を実行します。これらのメモリ領域は、オペレーティング システムに関する限り、連続したバイト シーケンスです。したがって、バイト バッファのみが I/O 操作に参加できるのは当然のことです。また、オペレーティング システムがプロセス (この場合は JVM プロセス) のアドレス空間に直接アクセスして、データを転送することも思い出してください。つまり、I/O 操作の対象となるメモリ領域は、連続したバイト シーケンスである必要があります。JVM では、バイトの配列がメモリに連続して格納されない場合や、ガベージ コレクターがいつでも移動する可能性があります。配列は Java のオブジェクトであり、そのオブジェクト内にデータを格納する方法は、JVM 実装ごとに異なる場合があります。

このため、ダイレクト バッファの概念が導入されました。ダイレクト バッファは、チャネルおよびネイティブ I/O ルーチンとの対話を目的としています。これらは、ネイティブ コードを使用してオペレーティング システムにメモリ領域を直接ドレインまたはフィルするように指示することにより、チャネルが直接または生のアクセスに使用できるメモリ領域にバイト要素を格納するために最善を尽くします。

直接バイト バッファは、通常、I/O 操作に最適な選択です。設計上、これらは JVM で使用できる最も効率的な I/O メカニズムをサポートします。非直接バイト バッファーをチャネルに渡すことはできますが、そうするとパフォーマンスが低下する可能性があります。通常、非ダイレクト バッファーをネイティブ I/O 操作のターゲットにすることはできません。非直接 ByteBuffer オブジェクトを書き込み用にチャネルに渡すと、チャネルは呼び出しごとに暗黙的に次のことを行う場合があります。

  1. 一時的な直接 ByteBuffer オブジェクトを作成します。
  2. 間接バッファーの内容を一時バッファーにコピーします。
  3. 一時バッファを使用して低レベルの I/O 操作を実行します。
  4. 一時バッファ オブジェクトはスコープ外になり、最終的にガベージ コレクションが実行されます。

これにより、すべての I/O でバッファ コピーとオブジェクト チャーンが発生する可能性がありますが、これはまさに避けたいことです。ただし、実装によっては、それほど悪くない場合もあります。ランタイムは、ダイレクト バッファをキャッシュして再利用するか、他の巧妙なトリックを実行してスループットを向上させる可能性があります。1 回限りの使用のためにバッファを作成するだけの場合、その違いは重要ではありません。一方、高パフォーマンスのシナリオでバッファーを繰り返し使用する場合は、直接バッファーを割り当てて再利用することをお勧めします。

ダイレクト バッファーは I/O に最適ですが、非ダイレクト バイト バッファーよりも作成コストが高くなる可能性があります。ダイレクト バッファで使用されるメモリは、標準の JVM ヒープをバイパスして、オペレーティング システム固有のネイティブ コードを呼び出すことによって割り当てられます。ホスト オペレーティング システムと JVM の実装によっては、ダイレクト バッファーの設定と破棄は、ヒープ常駐バッファーよりも大幅にコストがかかる可能性があります。ダイレクト バッファのメモリ ストレージ領域は、標準の JVM ヒープの外部にあるため、ガベージ コレクションの対象にはなりません。

直接バッファと非直接バッファの使用によるパフォーマンスのトレードオフは、JVM、オペレーティング システム、およびコード設計によって大きく異なります。ヒープの外にメモリを割り当てることで、JVM が認識していない追加の力にアプリケーションがさらされる可能性があります。可動部分を追加するときは、目的の効果が得られていることを確認してください。私は古いソフトウェアの格言をお勧めします。まずそれを機能させ、次に高速化します。前もって最適化についてあまり心配する必要はありません。まず正しさに集中する。JVM の実装では、バッファー キャッシングやその他の最適化を実行できる場合があります。これらの最適化により、必要なパフォーマンスが得られますが、多くの不必要な労力は必要ありません。

于 2011-04-15T02:48:22.017 に答える
26

jvmのアクセスでは、ダイレクト バッファの方が高速であると期待する理由はありません。それらの利点は、ネイティブ コード (あらゆる種類のコード ビハインド チャネルなど) に渡すときに得られます。

于 2011-04-15T01:20:24.727 に答える
22

DirectByteBuffersはOSレベルでの直接メモリマッピングであるため

そうではありません。これらは通常のアプリケーションプロセスメモリですが、Java GC中に再配置されることはなく、JNIレイヤー内の処理が大幅に簡素化されます。あなたが説明することはに適用されMappedByteBufferます。

get/put呼び出しでより高速に実行されること

結論は、前提からは得られません。前提は誤りです。結論も誤りです。JNIレイヤーに入ると高速になり、同じレイヤーから読み取りと書き込みを行う場合はDirectByteBuffer、データがJNI境界をまったく越える必要がないため、はるかに高速になります。

于 2011-04-17T01:56:33.350 に答える
19

自分で測定するのが最善です。簡単な答えは、バッファからの送信は、サイズに応じてallocateDirect()、バリアント (ファイルを /dev/null にコピーするようにテスト) よりも 25% から 75% 短い時間で済むようですが、割り当て自体は大幅に遅くなる可能性があります ( 100 倍)。allocate()

ソース:

于 2011-04-15T00:18:40.643 に答える