Podczas przetwarzania wielu gigabajtowych plików zauważyłem coś dziwnego: wygląda na to, że odczytywanie pliku za pomocą filechannelu do ponownie używanego obiektu ByteBuffer przydzielonego za pomocą allocateDirect jest znacznie wolniejsze niż czytanie z MappedByteBuffer, w rzeczywistości jest to nawet wolniej niż czytanie w tablicach bajtowych za pomocą zwykłych odczytów!Problem z wydajnością Java ByteBuffer
Spodziewałem się, że będzie on (prawie) tak szybki, jak odczyt z mappedbytebuffers, ponieważ mój ByteBuffer jest alokowany z allocateDirect, stąd odczyt powinien kończyć się bezpośrednio w moim buforze bajtowym bez żadnych kopii pośrednich.
Moje pytanie brzmi: co takiego robię źle? Czy też jest bajtbuffer + filechannel naprawdę mniejszy niż zwykły io/mmap?
I przykładowy kod poniżej Dodałem także kod, który konwertuje to, co czyta się na długie wartości, ponieważ tak właśnie robi to mój prawdziwy kod. Spodziewam się, że metoda ByteBuffer getLong() jest znacznie szybsza niż mój własny shuffeler bajtów.
Test-Wyniki: mmap: 3,828 ByteBuffer: 55,097 regularne 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);
}
}
jak załadunek dużych sekcje, a następnie przetwarzanie jest im nie jest rozwiązaniem (będę czytaj dane w całym miejscu) Myślę, że powinienem trzymać się MappedByteBuffer. Dziękuję wszystkim za sugestie.
To rzeczywiście byłoby szybciej. Nie spodziewałem się, że będzie o wiele szybciej, dzięki! –
Jeśli się nie mylę, regularna sekcja i/o zamierza używać bufora3 w obu pętlach, zamiast czytać długie fragmenty niezmiennego bufora1. –