172

ファイルシステムへのファイルの読み取りと書き込みにnioFileChannelと通常を使用した場合に、パフォーマンス (または利点) に違いがあるかどうかを調べようとしています。FileInputStream/FileOuputStream私のマシンでは、どちらも同じレベルで実行され、多くの場合、FileChannel速度が低下することがわかりました。これら2つの方法を比較して詳細を教えてください。これが私が使用したコードです。私がテストしているファイルは around 350MB. ランダムアクセスやその他の高度な機能を検討していない場合、ファイル I/O に NIO ベースのクラスを使用することは適切なオプションですか?

package trialjavaprograms;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class JavaNIOTest {
    public static void main(String[] args) throws Exception {
        useNormalIO();
        useFileChannel();
    }

    private static void useNormalIO() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        InputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        byte[] buf = new byte[64 * 1024];
        int len = 0;
        while((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fos.flush();
        fos.close();
        is.close();
        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }

    private static void useFileChannel() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        FileInputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        FileChannel f = is.getChannel();
        FileChannel f2 = fos.getChannel();

        ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
        long len = 0;
        while((len = f.read(buf)) != -1) {
            buf.flip();
            f2.write(buf);
            buf.clear();
        }

        f2.close();
        f.close();

        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }
}
4

7 に答える 7

209

より大きなファイル サイズでの私の経験でjava.nioは、java.io. しっかり速く。 >250% の範囲のように。そうは言っても、マイクロベンチマークが影響を受ける可能性があることを示唆する明らかなボトルネックを排除しています。調査の潜在的な領域:

バッファサイズ。 基本的に持っているアルゴリズムは

  • ディスクからバッファにコピー
  • バッファからディスクにコピー

私自身の経験では、このバッファ サイズは調整の機が熟しているということです。アプリケーションの一部に 4KB、別の部分に 256KB を設定しました。あなたのコードは、このような大きなバッファで苦しんでいると思われます。1KB、2KB、4KB、8KB、16KB、32KB、および 64KB のバッファーでいくつかのベンチマークを実行して、それを証明してください。

同じディスクに対して読み取りと書き込みを行う Java ベンチマークを実行しないでください。

もしそうなら、あなたは本当にディスクをベンチマークしていて、Java ではありません。また、CPU がビジーでない場合は、他のボトルネックが発生している可能性があります。

必要がない場合は、バッファーを使用しないでください。

ターゲットが別のディスクまたは NIC である場合、なぜメモリにコピーするのでしょうか? より大きなファイルでは、発生する遅延は自明ではありません。

他の人が言ったように、FileChannel.transferTo()orを使用してFileChannel.transferFrom()ください。ここでの主な利点は、JVM が OS の DMA ( Direct Memory Access ) へのアクセス (存在する場合) を使用することです。(これは実装に依存しますが、汎用 CPU 上の最新の Sun および IBM バージョンは問題ありません。) 何が起こるかというと、データはディスクとの間で直接やり取りされ、バスへ、そして目的地へと転送されます...任意の回路をバイパスしてRAMまたはCPU。

私が昼夜を問わず取り組んできた Web アプリは、非常に重い IO です。マイクロ ベンチマークと実世界のベンチマークも行っています。結果は私のブログにアップされています。

本番データと環境を使用する

マイクロベンチマークは歪みやすい。可能であれば、予想されるハードウェアで、予想される負荷で、実行しようと計画していることから正確にデータを収集する努力をしてください。

私のベンチマークは堅実で信頼できるものです。なぜなら、ベンチマークは本番システム、強力なシステム、負荷のかかるシステムでログに収集されたものだからです。 JVM が私のハードディスクを動かしているのを熱心に見ている間、私のノートブックの 7200 RPM 2.5 インチ SATA ドライブ ではありませんでした。

あなたは何を実行していますか?それは重要です。

于 2009-11-06T21:48:33.427 に答える
39

比較したいのがファイル コピーのパフォーマンスである場合、チャネル テストでは代わりに次のようにする必要があります。

final FileInputStream inputStream = new FileInputStream(src);
final FileOutputStream outputStream = new FileOutputStream(dest);
final FileChannel inChannel = inputStream.getChannel();
final FileChannel outChannel = outputStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
inputStream.close();
outputStream.close();

これは、あるチャネルから別のチャネルに自分自身をバッファリングするよりも遅くはなく、潜在的に非常に高速になります。Javadocs によると:

多くのオペレーティング システムは、バイトを実際にコピーせずに、ファイル システム キャッシュからターゲット チャネルに直接転送できます。

于 2009-10-22T10:11:33.503 に答える
7

私のテスト (Win7 64bit、6GB RAM、Java6) に基づくと、NIO transferFrom は小さなファイルでのみ高速で、大きなファイルでは非常に遅くなります。NIO データバッファ フリップは常に標準 IO よりも優れています。

  • 1000x2MBをコピー中

    1. NIO (転送元) ~2300ms
    2. NIO (直接 datababuffer 5000b フリップ) ~3500ms
    3. 標準 IO (バッファ 5000b) ~6000ms
  • 100x20mb をコピーしています

    1. NIO (直接 datababuffer 5000b フリップ) ~4000ms
    2. NIO (転送元) ~5000ms
    3. 標準 IO (バッファ 5000b) ~6500ms
  • 1x1000mb をコピーしています

    1. NIO (直接 datababuffer 5000b フリップ) ~4500 秒
    2. 標準 IO (バッファ 5000b) ~7000ms
    3. NIO (転送元) ~8000ms

transferTo() メソッドは、ファイルのチャンクに対して機能します。高レベルのファイル コピー方法として意図されたものではありませんでした: Windows XP で大きなファイルをコピーする方法は?

于 2011-11-03T14:33:19.523 に答える
2

transferTo 機能または非ブロッキング機能を使用していない場合、従来の IO は NIO にマップされるため、従来の IO と NIO(2) の違いに気付かないでしょう。

ただし、transferFrom/To などの NIO 機能を使用できる場合、またはバッファーを使用したい場合は、もちろん NIO が最適です。

于 2013-04-22T16:57:54.460 に答える