При обработке нескольких гигабайтных файлов я заметил нечто странное: кажется, что чтение из файла с использованием файлового канала в повторно используемый объект ByteBuffer, выделенный с помощью allocateDirect, намного медленнее, чем чтение из MappedByteBuffer, на самом деле это даже медленнее, чем чтение в байтовые массивы с использованием регулярных вызовов чтения!
Я ожидал, что это будет (почти) так же быстро, как чтение из mappedbytebuffers, поскольку мой ByteBuffer выделен с помощью allocateDirect, поэтому чтение должно закончиться непосредственно в моем байтовом буфере без каких-либо промежуточных копий.
Мой вопрос сейчас такой: что я делаю не так? Или bytebuffer + filechannel действительно медленнее, чем обычный io / mmap?
В приведенном ниже примере кода я также добавил некоторый код, который преобразует то, что читается, в длинные значения, поскольку это то, чем постоянно занимается мой настоящий код. Я ожидаю, что метод getLong () ByteBuffer намного быстрее, чем мой собственный shuffeler байтов.
Тест-результаты:
Mmap: 3,828
байтбуфер: 55,097
обычный ввод / вывод: 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.
Спасибо всем за ваши предложения.