web-dev-qa-db-ja.com

大きなファイルをインクリメンタルに読み取る最速の方法

MAX_BUFFER_SIZEのバッファーと、それをはるかに超えるファイルが与えられた場合、どのようにして次のことができますか。

  1. MAX_BUFFER_SIZEのブロックでファイルを読み取りますか?
  2. できるだけ早く

NIOを使ってみた

    RandomAccessFile aFile = new RandomAccessFile(fileName, "r");
    FileChannel inChannel = aFile.getChannel();

    ByteBuffer buffer = ByteBuffer.allocate(CAPARICY);

    int bytesRead = inChannel.read(buffer);

    buffer.flip();

        while (buffer.hasRemaining()) {
            buffer.get();
        }

        buffer.clear();
        bytesRead = inChannel.read(buffer);

    aFile.close();

そして定期的なIO

    InputStream in = new FileInputStream(fileName);

    long length = fileName.length();

    if (length > Integer.MAX_VALUE) {
        throw new IOException("File is too large!");
    }

    byte[] bytes = new byte[(int) length];

    int offset = 0;

    int numRead = 0;

    while (offset < bytes.length
            && (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) {
        offset += numRead;
    }

    if (offset < bytes.length) {
        throw new IOException("Could not completely read file " + fileName);
    }

    in.close();

regular IOはNIOと同じことを行うと約100倍速くなりますであることがわかります。バッファチャンクのファイル?

最終的に私は大きなファイルで作業していますが、一度にすべてを読み取るためのメモリがありません。代わりに、それを処理に使用するブロックで段階的に読み取りたいと思います。

19
James Raitsev

(現在行っているように)ファイル全体を一度にメモリに読み込む必要があると仮定すると、小さいチャンクの読み込みもNIOもここでは役に立ちません。

実際、おそらくより大きなチャンクを読むのが一番良いでしょう-通常のIOコードが自動的にやってくれます。

buffer.get();を使用して一度に1バイトしか読み取っていないため、NIOコードは現在低速です。

チャンクで処理したい場合-たとえば、ストリーム間の転送-ここでは、NIOなしで行う標準的な方法を示します。

InputStream is = ...;
OutputStream os = ...;

byte buffer[] = new byte[1024];
int read;
while((read = is.read(buffer)) != -1){
    os.write(buffer, 0, read);
}

これは1 KBのバッファサイズを使用しますが、無制限の量のデータを転送できます。

(機能レベルで実際に実行しようとしていることの詳細で回答を拡張する場合、これをさらに改善してより良い回答にすることができます。)

20
ziesemer

最初の例をより速くしたい場合

FileChannel inChannel = new FileInputStream(fileName).getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(CAPACITY);

while(inChannel.read(buffer) > 0)
    buffer.clear(); // do something with the data and clear/compact it.

inChannel.close();

もっと速くしたいなら。

FileChannel inChannel = new RandomAccessFile(fileName, "r").getChannel();
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
// access the buffer as you wish.
inChannel.close();

サイズが最大2 GBのファイルの場合、10〜20マイクロ秒かかることがあります。

23
Peter Lawrey