一部の JVM/OS の組み合わせで、java.nio.FileChannel transferTo() および transferFrom() がバイトごとの転送 (ストリームベースまたは ByteBuffer を使用) よりも速いのはなぜですか?
これらの方法は、バイト転送ごとに割り込み要求 (IRQ) を発行するのではなく、ダイレクト メモリ アクセス (DMA) を使用しますか??
一部の JVM/OS の組み合わせで、java.nio.FileChannel transferTo() および transferFrom() がバイトごとの転送 (ストリームベースまたは ByteBuffer を使用) よりも速いのはなぜですか?
これらの方法は、バイト転送ごとに割り込み要求 (IRQ) を発行するのではなく、ダイレクト メモリ アクセス (DMA) を使用しますか??
これらの方法は、バイト転送ごとに割り込み要求 (IRQ) を発行するのではなく、ダイレクト メモリ アクセス (DMA) を使用しますか?
詳細は実際の実装次第であり、特定のメカニズムを使用する必要はありません。transferTo
これは、 (強調鉱山)のドキュメントで示唆されています:
この方法は、このチャネルから読み取り、ターゲットチャネルに書き込む単純なループよりもはるかに効率的である可能性があります。多くのオペレーティング システムは、バイトを実際にコピーせずに、ファイル システム キャッシュからターゲット チャネルに直接転送できます。
「潜在的に」、「多く」…保証はありません。
JVM がネイティブ コードを介してショートカットできるようにするため (サポートされているチャネル タイプを使用する場合)、このメソッドを使用する方がおそらく効率的であると想定することは理にかなっています。彼らが説明する「単純なループ」は、次のように機能します。
ByteBuffer buf = ByteBuffer.allocateDirect(BUF_SIZE);
while ( /* read more condition */ ) {
source.read(buf);
buf.flip();
target.write(buf);
buf.compact();
}
このスニペットはダイレクト バッファを使用していますが、バッファ管理 (読み取り、反転、書き込み、圧縮) のために Java に戻っていることに注意してください。最適化コンパイラはその一部を省略できる場合がありますが、おそらくそうではありません。
transferTo
ただし、 /を使用transferFrom
すると、バイトの転送方法を決定するのは JVM に任されます。プラットフォームがこの種の転送をネイティブにサポートしている場合、中間バッファーを作成せずに転送できる場合があります。そのようなサポートがない場合でも、JVM は上記のようなループを実装できます。
例: SocketChannel が を介して FileChannel から直接データを読み取るように指示されたとしますtransferFrom
。FileChannel は最近読み取られ、その内容は OS ファイル キャッシュにあります。バイトを読み取ってバッファにコピーするのではなく、SocketChannel は OS ファイル キャッシュを直接ポイントして、そこから送信を開始できます。少なくとも 1 ラウンドのコピーが排除されました。
さらに、ソケット (A) が、たとえば SocketChannel B と呼ばれる一種のパイプを使用して、実際に他のローカル プロセスに接続されているとします。B が A から取得したものを読み取り始めるとき、実際には OS ファイルから直接読み取っている可能性があります。再度キャッシュします。その後、BtransferTo
が別のチャネルを使用する場合... アイデアが得られます。
次の記事では、ゼロコピーについて
https://www.ibm.com/developerworks/linux/library/j-zerocopy/
著者は、新しい方法は Linux でより効率的であると説明しています。これは、コンテキスト スイッチが減り、カーネルからアプリケーションへの不要なバッファ コピーが減るためです。
DMA/ゼロコピーを使用するため、「From」バッファから CPU への転送と、CPU から「to」バッファへの転送が節約されます。より詳細な説明については、IBMのこの記事を参照してください。