2017-02-20 20 views
4

DNG/TIFFファイルのリーダ/ライターで作業しています。一般的なファイル(FileInputStreamFileChannelRandomAccessFile)を扱ういくつかのオプションがあるので、どの戦略が自分のニーズに合っているのだろうかと思っています。画像の(1-3)Javaの複数のファイルから分散データを読み取る

  • 一部(5-20​​)の小ブロック(数十百バイト)
  • 非常にいくつかの大きな連続ブロック:

    DNG/TIFFファイルは、の組成物であり、全体的なファイルサイズが約15までのMIB(圧縮された14ビットの生データ)の範囲

  • いくつかの(多分20-50)非常に小さなブロック(4-16バイト)

(100 MIBへアップ)データ100 MiB(非圧縮フロートデータ)。処理するファイルの数は50〜400です。

2つの使用パターンがあります。

  1. 読むすべてのファイルのすべてのメタデータ(画像データ以外のすべて)
  2. 読むには、すべてのファイルからのすべての画像データが私は

現在はFileChannelを使用し、map()を実行してファイル全体をカバーするMappedByteBufferを取得します。私がメタデータを読むことに興味があれば、これはかなり無駄に思えます。別の問題は、マップされたメモリを解放していることです。パージングなどのためにマップされたバッファのスライスを渡すと、基底のMappedByteBufferは収集されません。

今度はFileChannelの小さなチャンクをいくつかのread()のメソッドを使用してコピーし、大きな生データ領域のみをマップすることにしました。欠点は何readShort()はありませんので、単一の値の読み取りが非常に複雑なようだということですなど:

short readShort(long offset) throws IOException, InterruptedException { 
    return read(offset, Short.BYTES).getShort(); 
} 

ByteBuffer read(long offset, long byteCount) throws IOException, InterruptedException { 
    ByteBuffer buffer = ByteBuffer.allocate(Math.toIntExact(byteCount)); 
    buffer.order(GenericTiffFileReader.this.byteOrder); 
    GenericTiffFileReader.this.readInto(buffer, offset); 
    return buffer; 
} 

private void readInto(ByteBuffer buffer, long startOffset) 
     throws IOException, InterruptedException { 

    long offset = startOffset; 
    while (buffer.hasRemaining()) { 
     int bytesRead = this.channel.read(buffer, offset); 
     switch (bytesRead) { 
     case 0: 
      Thread.sleep(10); 
      break; 
     case -1: 
      throw new EOFException("unexpected end of file"); 
     default: 
      offset += bytesRead; 
     } 
    } 
    buffer.flip(); 
} 

RandomAccessFilereadShort()またはreadFully()のような便利な方法を提供しますが、リトルエンディアンバイト順を扱うことができません。

したがって、1バイトと巨大ブロックの分散読み取りを処理するための慣用的な方法はありますか? 100ByBのファイル全体をメモリマッピングするだけで、何百バイトもの読み込みや読み込みができますか?

+0

すべての読み取りに1つのByteBufferを使用します。それらを作成するのはかなり高価です。 – EJP

+0

サイズが動的であり、同時使用が禁止されるため、最終アプリケーションでは 'ByteBuffer'を事前に割り当てることはできません。 –

答えて

0

[OK]を、私は最終的にいくつかの大まかなベンチマークをした:

  1. フラッシュすべてがecho 3 > /proc/sys/vm/drop_caches
  2. 繰り返し8回のキャッシュを読む:各ファイルから1000倍8つのバイトを読む(1ジブまで20 MIBから約20ファイル)。

ファイルサイズの合計がインストールされているシステムメモリを超えています。

方法1、FileChannelと一時ByteBuffer S:

private static long method1(Path file, long dummyUsage) throws IOException, Error { 
    try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) { 

     for (int i = 0; i < 1000; i++) { 
      ByteBuffer dst = ByteBuffer.allocate(8); 

      if (channel.position(i * 10000).read(dst) != dst.capacity()) 
       throw new Error("partial read"); 
      dst.flip(); 
      dummyUsage += dst.order(ByteOrder.LITTLE_ENDIAN).getInt(); 
      dummyUsage += dst.order(ByteOrder.BIG_ENDIAN).getInt(); 
     } 
    } 
    return dummyUsage; 
} 

結果:

private static long method2(Path file, long dummyUsage) throws IOException { 

    final MappedByteBuffer buffer; 
    try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) { 
     buffer = channel.map(MapMode.READ_ONLY, 0L, Files.size(file)); 
    } 
    for (int i = 0; i < 1000; i++) { 
     dummyUsage += buffer.order(ByteOrder.LITTLE_ENDIAN).getInt(i * 10000); 
     dummyUsage += buffer.order(ByteOrder.BIG_ENDIAN).getInt(i * 10000 + 4); 
    } 
    return dummyUsage; 
} 

結果::

1. 3422 ms 
2. 56 ms 
3. 24 ms 
4. 24 ms 
5. 27 ms 
6. 25 ms 
7. 23 ms 
8. 23 ms 

方法2、MappedByteBufferファイル全体をカバー

1. 749 ms 
2. 21 ms 
3. 17 ms 
4. 16 ms 
5. 18 ms 
6. 13 ms 
7. 15 ms 
8. 17 ms 

方法3、RandomAccessFile

private static long method3(Path file, long dummyUsage) throws IOException { 

    try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "r")) { 
     for (int i = 0; i < 1000; i++) { 

      raf.seek(i * 10000); 
      dummyUsage += Integer.reverseBytes(raf.readInt()); 
      raf.seek(i * 10000 + 4); 
      dummyUsage += raf.readInt(); 
     } 
    } 
    return dummyUsage; 
} 

結果:

1. 3479 ms 
2. 104 ms 
3. 81 ms 
4. 84 ms 
5. 78 ms 
6. 81 ms 
7. 81 ms 
8. 81 ms 

結論:MappedByteBuffer -methodは(代わりに140メガバイト340 MB)以上のページキャッシュメモリを占有するが、上の有意に良好行います最初とすべての後続の実行と最低のオーバーヘッドを持っているようだ。ボーナスとして、このメソッドは、バイトオーダー、分散した小さなデータ、および巨大なデータブロックに関して、本当に快適なインターフェースを提供します。 RandomAccessFileが最悪です。

私自身の質問に答えるには:ファイル全体をカバーするMappedByteBufferは、メモリに関して無駄なことなく、大きなファイルへのランダムアクセスを処理するための慣用的かつ最速の方法です。

関連する問題