Mientras procesaba varios archivos de gigabytes noté algo extraño: parece que leer un archivo utilizando un canal de archivos en un objeto ByteBuffer reutilizado asignado con allocateDirect es mucho más lento que leer desde un MappedByteBuffer, de hecho es ¡incluso más lento que leer en matrices de bytes usando llamadas de lectura regulares!Problema de rendimiento de Java ByteBuffer
Esperaba que fuera (casi) tan rápido como la lectura de mappedbytebuffers ya que mi ByteBuffer está asignado con allocateDirect, por lo tanto, la lectura debe terminar directamente en mi bytebuffer sin ninguna copia intermedia.
Mi pregunta ahora es: ¿qué es lo que estoy haciendo mal? ¿O es bytebuffer + filechannel realmente más lento que el io/mmap regular?
I el código de ejemplo a continuación También agregué un código que convierte lo que se lee en valores largos, ya que es lo que mi código real hace constantemente. Esperaría que el método byteBuffer getLong() sea mucho más rápido que mi propio shuffeler de bytes.
Test-resultados: mmap: 3.828 ByteBuffer: 55.097 regulares de E/S: 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);
}
}
Como cargar grandes secciones y luego procesarlos es no es una opción (voy estar leyendo datos en todo el lugar) Creo que debería mantener un MappedByteBuffer. Gracias a todos por sus sugerencias.
Eso sería más rápido. No esperaba que fuera mucho más rápido, ¡gracias! –
Si no me equivoco, la sección de E/S regular tiene la intención de utilizar buffer3 en ambos bucles, en lugar de leer longs del buffer1 sin cambios. –