【发布时间】:2011-12-06 00:39:16
【问题描述】:
在处理多个千兆字节文件时,我注意到一些奇怪的事情:似乎使用文件通道从文件读取到使用 allocateDirect 分配的重用 ByteBuffer 对象比从 MappedByteBuffer 读取要慢得多,实际上它甚至比读取还要慢使用常规读取调用进入字节数组!
我希望它(几乎)与从 mappedbytebuffers 读取一样快,因为我的 ByteBuffer 是使用 allocateDirect 分配的,因此读取应该直接在我的 bytebuffer 中结束,而无需任何中间副本。
我现在的问题是:我做错了什么?或者 bytebuffer+filechannel 真的比普通的 io/mmap 慢吗?
在下面的示例代码中,我还添加了一些将读取的内容转换为长值的代码,因为这就是我的真实代码经常做的事情。我希望 ByteBuffer getLong() 方法比我自己的字节洗牌器快得多。
测试结果: 地图:3.828 字节缓冲区:55.097 常规 i/o:38.175
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.MappedByteBuffer;
class testbb {
static final int size = 536870904, n = size / 24;
static public long byteArrayToLong(byte [] in, int offset) {
return ((((((((long)(in[offset + 0] & 0xff) << 8) | (long)(in[offset + 1] & 0xff)) << 8 | (long)(in[offset + 2] & 0xff)) << 8 | (long)(in[offset + 3] & 0xff)) << 8 | (long)(in[offset + 4] & 0xff)) << 8 | (long)(in[offset + 5] & 0xff)) << 8 | (long)(in[offset + 6] & 0xff)) << 8 | (long)(in[offset + 7] & 0xff);
}
public static void main(String [] args) throws IOException {
long start;
RandomAccessFile fileHandle;
FileChannel fileChannel;
// create file
fileHandle = new RandomAccessFile("file.dat", "rw");
byte [] buffer = new byte[24];
for(int index=0; index<n; index++)
fileHandle.write(buffer);
fileChannel = fileHandle.getChannel();
// mmap()
MappedByteBuffer mbb = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
byte [] buffer1 = new byte[24];
start = System.currentTimeMillis();
for(int index=0; index<n; index++) {
mbb.position(index * 24);
mbb.get(buffer1, 0, 24);
long dummy1 = byteArrayToLong(buffer1, 0);
long dummy2 = byteArrayToLong(buffer1, 8);
long dummy3 = byteArrayToLong(buffer1, 16);
}
System.out.println("mmap: " + (System.currentTimeMillis() - start) / 1000.0);
// bytebuffer
ByteBuffer buffer2 = ByteBuffer.allocateDirect(24);
start = System.currentTimeMillis();
for(int index=0; index<n; index++) {
buffer2.rewind();
fileChannel.read(buffer2, index * 24);
buffer2.rewind(); // need to rewind it to be able to use it
long dummy1 = buffer2.getLong();
long dummy2 = buffer2.getLong();
long dummy3 = buffer2.getLong();
}
System.out.println("bytebuffer: " + (System.currentTimeMillis() - start) / 1000.0);
// regular i/o
byte [] buffer3 = new byte[24];
start = System.currentTimeMillis();
for(int index=0; index<n; index++) {
fileHandle.seek(index * 24);
fileHandle.read(buffer3);
long dummy1 = byteArrayToLong(buffer1, 0);
long dummy2 = byteArrayToLong(buffer1, 8);
long dummy3 = byteArrayToLong(buffer1, 16);
}
System.out.println("regular i/o: " + (System.currentTimeMillis() - start) / 1000.0);
}
}
由于加载大部分然后处理它们不是一个选项(我将在整个地方读取数据)我认为我应该坚持使用 MappedByteBuffer。 谢谢大家的建议。
【问题讨论】:
标签: java performance nio bytebuffer