在處理多個千兆字節文件時,我注意到一些奇怪的現象:似乎從使用filechannel的文件讀取分配給allocateDirect的重用ByteBuffer對象比從MappedByteBuffer讀取要慢得多,實際上它是甚至比使用常規讀取調用讀入字節數組更慢!Java ByteBuffer性能問題
由於我的ByteBuffer是用allocateDirect分配的,因此讀取應該直接在我的bytebuffer中直接結束,沒有任何中間拷貝,所以我期待它幾乎像讀取映射緩衝區一樣快。
我現在的問題是:我做錯了什麼?或者是bytebuffer + filechannel真的比普通的io/mmap慢嗎?
我下面的示例代碼我還添加了一些代碼,將讀取的內容轉換爲長整型值,因爲這是我真正的代碼不斷做的。我期望ByteBuffer getLong()方法比我自己的字節分離器快得多。
試驗結果: MMAP:3.828 的ByteBuffer: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。 謝謝大家的建議。
這確實會更快。沒有想到它會更快,所以謝謝! –
如果我沒有弄錯,常規I/O部分打算在兩個循環中使用buffer3,而不是從不變緩衝區中讀取多個long1。 –