¿La forma más rápida de escribir una matriz de enteros en un archivo en Java?

Como dice el título, estoy buscando la forma más rápida posible de escribir matrices de enteros en archivos. Las matrices variarán en tamaño y contendrán de manera realista entre 2500 y 25 000 000 ints.

Aquí está el código que estoy usando actualmente:

DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename))); for (int d : data) writer.writeInt(d); 

Dado que DataOutputStream tiene un método para escribir matrices de bytes, he intentado convertir la matriz int en una matriz de bytes como esta:

 private static byte[] integersToBytes(int[] values) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); for (int i = 0; i < values.length; ++i) { dos.writeInt(values[i]); } return baos.toByteArray(); } 

y así:

 private static byte[] integersToBytes2(int[] src) { int srcLength = src.length; byte[] dst = new byte[srcLength << 2]; for (int i = 0; i < srcLength; i++) { int x = src[i]; int j = i <>> 0) & 0xff); dst[j++] = (byte) ((x >>> 8) & 0xff); dst[j++] = (byte) ((x >>> 16) & 0xff); dst[j++] = (byte) ((x >>> 24) & 0xff); } return dst; } 

Ambos parecen dar un aumento de velocidad menor, alrededor del 5%. No los he probado con suficiente rigor para confirmarlo.

¿Existe alguna técnica que acelere la operación de escritura de este archivo, o guías relevantes sobre las mejores prácticas para el rendimiento de escritura de Java IO?

Eché un vistazo a tres opciones:

  1. Usando DataOutputStream ;
  2. Uso de ObjectOutputStream (para objetos Serializable , que int[] es); y
  3. Usando FileChannel .

Los resultados son

 DataOutputStream wrote 1,000,000 ints in 3,159.716 ms ObjectOutputStream wrote 1,000,000 ints in 295.602 ms FileChannel wrote 1,000,000 ints in 110.094 ms 

Así que la versión NIO es la más rápida. También tiene la ventaja de permitir ediciones, lo que significa que puede cambiar fácilmente un int mientras que ObjectOutputStream requiere leer la matriz completa, modificarla y escribirla en un archivo.

El código sigue:

 private static final int NUM_INTS = 1000000; interface IntWriter { void write(int[] ints); } public static void main(String[] args) { int[] ints = new int[NUM_INTS]; Random r = new Random(); for (int i=0; i 

Utilizaría FileChannel del paquete nio y ByteBuffer . Este enfoque parece (en mi computadora) dar un rendimiento de escritura 2 a 4 veces mejor :

Salida del progtwig:

 normal time: 2555 faster time: 765 

Este es el progtwig:

 public class Test { public static void main(String[] args) throws IOException { // create a test buffer ByteBuffer buffer = createBuffer(); long start = System.currentTimeMillis(); { // do the first test (the normal way of writing files) normalToFile(new File("first"), buffer.asIntBuffer()); } long middle = System.currentTimeMillis(); { // use the faster nio stuff fasterToFile(new File("second"), buffer); } long done = System.currentTimeMillis(); // print the result System.out.println("normal time: " + (middle - start)); System.out.println("faster time: " + (done - middle)); } private static void fasterToFile(File file, ByteBuffer buffer) throws IOException { FileChannel fc = null; try { fc = new FileOutputStream(file).getChannel(); fc.write(buffer); } finally { if (fc != null) fc.close(); buffer.rewind(); } } private static void normalToFile(File file, IntBuffer buffer) throws IOException { DataOutputStream writer = null; try { writer = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(file))); while (buffer.hasRemaining()) writer.writeInt(buffer.get()); } finally { if (writer != null) writer.close(); buffer.rewind(); } } private static ByteBuffer createBuffer() { ByteBuffer buffer = ByteBuffer.allocate(4 * 25000000); Random r = new Random(1); while (buffer.hasRemaining()) buffer.putInt(r.nextInt()); buffer.rewind(); return buffer; } } 

Creo que deberías considerar el uso de canales de archivos (la biblioteca java.nio) en lugar de secuencias simples (java.io). Un buen punto de partida es esta interesante discusión: Java NIO FileChannel versus FileOutputstream rendimiento / utilidad

y los comentarios relevantes a continuación.

¡Aclamaciones!

La mejora principal que puede tener para escribir int [] es a cualquiera;

  • boost el tamaño del buffer El tamaño es correcto para la mayoría de las transmisiones, pero el acceso a los archivos puede ser más rápido con un búfer más grande. Esto podría producir una mejora del 10-20%.

  • Usa NIO y un buffer directo. Esto le permite escribir valores de 32 bits sin convertirlos en bytes. Esto puede producir una mejora del 5%.

Por cierto: deberías poder escribir al menos 10 millones de valores int por segundo. Con el almacenamiento en caché de disco, aumenta esto a 200 millones por segundo.

La matriz es serializable: ¿no se puede usar writer.writeObject(data); ? Eso definitivamente va a ser más rápido que las llamadas individuales de writeInt .

Si tiene otros requisitos en el formato de datos de salida que la recuperación en int[] , esa es una pregunta diferente.