我正在研究DNG / TIFF文件的读写器。由于有一些选项可以处理一般的文件(FileInputStream
,FileChannel
,RandomAccessFile
),我想知道哪种策略符合我的需求。
DNG / TIFF文件的组成为:
整个文件大小范围从15 MiB(压缩的14位原始数据)到大约100 MiB(未压缩的浮点数据)。要处理的文件数为50-400。
有两种使用模式:
我目前正在使用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();
}
RandomAccessFile
提供了有用的方法,如readShort()
或readFully()
,但无法处理小端字节顺序。
那么,是否有一种惯用的方法来处理单个字节和大块的分散读取?内存映射整个100 MiB文件只读取几百个字节是浪费还是慢?
答案 0 :(得分:0)
好的,我终于做了一些粗略的基准测试:
echo 3 > /proc/sys/vm/drop_caches
文件的总和'大小超过了我安装的系统内存。
方法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;
}
结果:
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
覆盖整个文件:
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. 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
- 方法占用更多的页面缓存内存(340 MB而不是140 MB),但在第一次和所有后续运行中表现更好,并且似乎具有最低的开销。作为奖励,这种方法提供了关于字节顺序,分散的小数据和巨大数据块的非常舒适的界面。 RandomAccessFile
表现最差。
回答我自己的问题:覆盖整个文件的MappedByteBuffer
似乎是处理大文件随机访问的惯用且最快的方式,而不会浪费内存。