2009-06-08 10 views
15

Tengo dos (2 GB cada uno) archivos en mi disco duro y quiero compararlos entre sí:Java archivos de gran tamaño S de disco Rendimiento

  • copiar los archivos originales con el explorador de Windows dura aprox. 2-4 minutos (es decir, leer y escribir, en el mismo disco físico y lógico).
  • La lectura con java.io.FileInputStream dos veces y la comparación de las matrices de bytes en bytes por byte lleva más de 20 minutos.
  • java.io.BufferedInputStream buffer es 64kb, los archivos se leen en trozos y luego se comparan.
  • La comparación que se hace es un bucle apretado como

    int numRead = Math.min(numRead[0], numRead[1]); 
    for (int k = 0; k < numRead; k++) 
    { 
        if (buffer[1][k] != buffer[0][k]) 
        { 
         return buffer[0][k] - buffer[1][k]; 
        } 
    } 
    

¿Qué puedo hacer para acelerar este proceso? ¿Se supone que NIO es más rápido que las transmisiones planas? ¿No puede Java utilizar las tecnologías DMA/SATA y, en su lugar, realiza algunas llamadas lentas a la API de sistema operativo?

EDIT:
Gracias por las respuestas. Hice algunos experimentos basados ​​en ellos. Como Andreas mostró

secuencias o nio enfoques no son muy diferentes.
Más importante es el tamaño de búfer correcto.

Esto es confirmado por mis propios experimentos. Como los archivos se leen en grandes fragmentos, incluso los búferes adicionales (BufferedInputStream) no dan nada. Es posible optimizar la comparación y obtuve los mejores resultados con el despliegue de 32 veces, pero el tiempo que gasto en comparación es pequeño en comparación con la lectura de disco, por lo que la aceleración es pequeña. Parece que no hay nada que pueda hacer ;-(

+1

Nota: de manera predeterminada, el SO maneja las tecnologías DMA/SATA para todas las operaciones de E/S de archivos (bueno, en sistemas operativos modernos). –

Respuesta

13

Probé tres métodos diferentes para comparar dos archivos idénticos de 3,8 gb con tamaños de memoria intermedia entre 8 kb y 1 MB. el primer primer método utilizado solo dos flujos de entrada en el búfer

el segundo método utiliza un subproceso que lee en dos subprocesos diferentes y se compara en un tercero. esto obtuvo un rendimiento ligeramente superior a expensas de una alta utilización de la CPU. la gestión del subproceso de subprocesos implica una gran sobrecarga con esas tareas de ejecución corta.

el tercer enfoque utiliza nio, tal como fue anunciado por laginimaineb

como se puede ver, el enfoque general no difiere mucho. más importante es el tamaño de búfer correcto.

Lo extraño es que leo 1 byte menos usando subprocesos. No pude detectar el error difícil.

comparing just with two streams 
I was equal, even after 3684070360 bytes and reading for 704813 ms (4,98MB/sec * 2) with a buffer size of 8 kB 
I was equal, even after 3684070360 bytes and reading for 578563 ms (6,07MB/sec * 2) with a buffer size of 16 kB 
I was equal, even after 3684070360 bytes and reading for 515422 ms (6,82MB/sec * 2) with a buffer size of 32 kB 
I was equal, even after 3684070360 bytes and reading for 534532 ms (6,57MB/sec * 2) with a buffer size of 64 kB 
I was equal, even after 3684070360 bytes and reading for 422953 ms (8,31MB/sec * 2) with a buffer size of 128 kB 
I was equal, even after 3684070360 bytes and reading for 793359 ms (4,43MB/sec * 2) with a buffer size of 256 kB 
I was equal, even after 3684070360 bytes and reading for 746344 ms (4,71MB/sec * 2) with a buffer size of 512 kB 
I was equal, even after 3684070360 bytes and reading for 669969 ms (5,24MB/sec * 2) with a buffer size of 1024 kB 
comparing with threads 
I was equal, even after 3684070359 bytes and reading for 602391 ms (5,83MB/sec * 2) with a buffer size of 8 kB 
I was equal, even after 3684070359 bytes and reading for 523156 ms (6,72MB/sec * 2) with a buffer size of 16 kB 
I was equal, even after 3684070359 bytes and reading for 527547 ms (6,66MB/sec * 2) with a buffer size of 32 kB 
I was equal, even after 3684070359 bytes and reading for 276750 ms (12,69MB/sec * 2) with a buffer size of 64 kB 
I was equal, even after 3684070359 bytes and reading for 493172 ms (7,12MB/sec * 2) with a buffer size of 128 kB 
I was equal, even after 3684070359 bytes and reading for 696781 ms (5,04MB/sec * 2) with a buffer size of 256 kB 
I was equal, even after 3684070359 bytes and reading for 727953 ms (4,83MB/sec * 2) with a buffer size of 512 kB 
I was equal, even after 3684070359 bytes and reading for 741000 ms (4,74MB/sec * 2) with a buffer size of 1024 kB 
comparing with nio 
I was equal, even after 3684070360 bytes and reading for 661313 ms (5,31MB/sec * 2) with a buffer size of 8 kB 
I was equal, even after 3684070360 bytes and reading for 656156 ms (5,35MB/sec * 2) with a buffer size of 16 kB 
I was equal, even after 3684070360 bytes and reading for 491781 ms (7,14MB/sec * 2) with a buffer size of 32 kB 
I was equal, even after 3684070360 bytes and reading for 317360 ms (11,07MB/sec * 2) with a buffer size of 64 kB 
I was equal, even after 3684070360 bytes and reading for 643078 ms (5,46MB/sec * 2) with a buffer size of 128 kB 
I was equal, even after 3684070360 bytes and reading for 865016 ms (4,06MB/sec * 2) with a buffer size of 256 kB 
I was equal, even after 3684070360 bytes and reading for 716796 ms (4,90MB/sec * 2) with a buffer size of 512 kB 
I was equal, even after 3684070360 bytes and reading for 652016 ms (5,39MB/sec * 2) with a buffer size of 1024 kB 

el código utilizado:

import junit.framework.Assert; 
import org.junit.Before; 
import org.junit.Test; 

import java.io.BufferedInputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.nio.ByteBuffer; 
import java.nio.channels.FileChannel; 
import java.text.DecimalFormat; 
import java.text.NumberFormat; 
import java.util.Arrays; 
import java.util.concurrent.*; 

public class FileCompare { 

    private static final int MIN_BUFFER_SIZE = 1024 * 8; 
    private static final int MAX_BUFFER_SIZE = 1024 * 1024; 
    private String fileName1; 
    private String fileName2; 
    private long start; 
    private long totalbytes; 

    @Before 
    public void createInputStream() { 
     fileName1 = "bigFile.1"; 
     fileName2 = "bigFile.2"; 
    } 

    @Test 
    public void compareTwoFiles() throws IOException { 
     System.out.println("comparing just with two streams"); 
     int currentBufferSize = MIN_BUFFER_SIZE; 
     while (currentBufferSize <= MAX_BUFFER_SIZE) { 
      compareWithBufferSize(currentBufferSize); 
      currentBufferSize *= 2; 
     } 
    } 

    @Test 
    public void compareTwoFilesFutures() 
      throws IOException, ExecutionException, InterruptedException { 
     System.out.println("comparing with threads"); 
     int myBufferSize = MIN_BUFFER_SIZE; 
     while (myBufferSize <= MAX_BUFFER_SIZE) { 
      start = System.currentTimeMillis(); 
      totalbytes = 0; 
      compareWithBufferSizeFutures(myBufferSize); 
      myBufferSize *= 2; 
     } 
    } 

    @Test 
    public void compareTwoFilesNio() throws IOException { 
     System.out.println("comparing with nio"); 
     int myBufferSize = MIN_BUFFER_SIZE; 
     while (myBufferSize <= MAX_BUFFER_SIZE) { 
      start = System.currentTimeMillis(); 
      totalbytes = 0; 
      boolean wasEqual = isEqualsNio(myBufferSize); 

      if (wasEqual) { 
       printAfterEquals(myBufferSize); 
      } else { 
       Assert.fail("files were not equal"); 
      } 

      myBufferSize *= 2; 
     } 

    } 

    private void compareWithBufferSize(int myBufferSize) throws IOException { 
     final BufferedInputStream inputStream1 = 
       new BufferedInputStream(
         new FileInputStream(new File(fileName1)), 
         myBufferSize); 
     byte[] buff1 = new byte[myBufferSize]; 
     final BufferedInputStream inputStream2 = 
       new BufferedInputStream(
         new FileInputStream(new File(fileName2)), 
         myBufferSize); 
     byte[] buff2 = new byte[myBufferSize]; 
     int read1; 

     start = System.currentTimeMillis(); 
     totalbytes = 0; 
     while ((read1 = inputStream1.read(buff1)) != -1) { 
      totalbytes += read1; 
      int read2 = inputStream2.read(buff2); 
      if (read1 != read2) { 
       break; 
      } 
      if (!Arrays.equals(buff1, buff2)) { 
       break; 
      } 
     } 
     if (read1 == -1) { 
      printAfterEquals(myBufferSize); 
     } else { 
      Assert.fail("files were not equal"); 
     } 
     inputStream1.close(); 
     inputStream2.close(); 
    } 

    private void compareWithBufferSizeFutures(int myBufferSize) 
      throws ExecutionException, InterruptedException, IOException { 
     final BufferedInputStream inputStream1 = 
       new BufferedInputStream(
         new FileInputStream(
           new File(fileName1)), 
         myBufferSize); 
     final BufferedInputStream inputStream2 = 
       new BufferedInputStream(
         new FileInputStream(
           new File(fileName2)), 
         myBufferSize); 

     final boolean wasEqual = isEqualsParallel(myBufferSize, inputStream1, inputStream2); 

     if (wasEqual) { 
      printAfterEquals(myBufferSize); 
     } else { 
      Assert.fail("files were not equal"); 
     } 
     inputStream1.close(); 
     inputStream2.close(); 
    } 

    private boolean isEqualsParallel(int myBufferSize 
      , final BufferedInputStream inputStream1 
      , final BufferedInputStream inputStream2) 
      throws InterruptedException, ExecutionException { 
     final byte[] buff1Even = new byte[myBufferSize]; 
     final byte[] buff1Odd = new byte[myBufferSize]; 
     final byte[] buff2Even = new byte[myBufferSize]; 
     final byte[] buff2Odd = new byte[myBufferSize]; 
     final Callable<Integer> read1Even = new Callable<Integer>() { 
      public Integer call() throws Exception { 
       return inputStream1.read(buff1Even); 
      } 
     }; 
     final Callable<Integer> read2Even = new Callable<Integer>() { 
      public Integer call() throws Exception { 
       return inputStream2.read(buff2Even); 
      } 
     }; 
     final Callable<Integer> read1Odd = new Callable<Integer>() { 
      public Integer call() throws Exception { 
       return inputStream1.read(buff1Odd); 
      } 
     }; 
     final Callable<Integer> read2Odd = new Callable<Integer>() { 
      public Integer call() throws Exception { 
       return inputStream2.read(buff2Odd); 
      } 
     }; 
     final Callable<Boolean> oddEqualsArray = new Callable<Boolean>() { 
      public Boolean call() throws Exception { 
       return Arrays.equals(buff1Odd, buff2Odd); 
      } 
     }; 
     final Callable<Boolean> evenEqualsArray = new Callable<Boolean>() { 
      public Boolean call() throws Exception { 
       return Arrays.equals(buff1Even, buff2Even); 
      } 
     }; 

     ExecutorService executor = Executors.newCachedThreadPool(); 
     boolean isEven = true; 
     Future<Integer> read1 = null; 
     Future<Integer> read2 = null; 
     Future<Boolean> isEqual = null; 
     int lastSize = 0; 
     while (true) { 
      if (isEqual != null) { 
       if (!isEqual.get()) { 
        return false; 
       } else if (lastSize == -1) { 
        return true; 
       } 
      } 
      if (read1 != null) { 
       lastSize = read1.get(); 
       totalbytes += lastSize; 
       final int size2 = read2.get(); 
       if (lastSize != size2) { 
        return false; 
       } 
      } 
      isEven = !isEven; 
      if (isEven) { 
       if (read1 != null) { 
        isEqual = executor.submit(oddEqualsArray); 
       } 
       read1 = executor.submit(read1Even); 
       read2 = executor.submit(read2Even); 
      } else { 
       if (read1 != null) { 
        isEqual = executor.submit(evenEqualsArray); 
       } 
       read1 = executor.submit(read1Odd); 
       read2 = executor.submit(read2Odd); 
      } 
     } 
    } 

    private boolean isEqualsNio(int myBufferSize) throws IOException { 
     FileChannel first = null, seconde = null; 
     try { 
      first = new FileInputStream(fileName1).getChannel(); 
      seconde = new FileInputStream(fileName2).getChannel(); 
      if (first.size() != seconde.size()) { 
       return false; 
      } 
      ByteBuffer firstBuffer = ByteBuffer.allocateDirect(myBufferSize); 
      ByteBuffer secondBuffer = ByteBuffer.allocateDirect(myBufferSize); 
      int firstRead, secondRead; 
      while (first.position() < first.size()) { 
       firstRead = first.read(firstBuffer); 
       totalbytes += firstRead; 
       secondRead = seconde.read(secondBuffer); 
       if (firstRead != secondRead) { 
        return false; 
       } 
       if (!nioBuffersEqual(firstBuffer, secondBuffer, firstRead)) { 
        return false; 
       } 
      } 
      return true; 
     } finally { 
      if (first != null) { 
       first.close(); 
      } 
      if (seconde != null) { 
       seconde.close(); 
      } 
     } 
    } 

    private static boolean nioBuffersEqual(ByteBuffer first, ByteBuffer second, final int length) { 
     if (first.limit() != second.limit() || length > first.limit()) { 
      return false; 
     } 
     first.rewind(); 
     second.rewind(); 
     for (int i = 0; i < length; i++) { 
      if (first.get() != second.get()) { 
       return false; 
      } 
     } 
     return true; 
    } 

    private void printAfterEquals(int myBufferSize) { 
     NumberFormat nf = new DecimalFormat("#.00"); 
     final long dur = System.currentTimeMillis() - start; 
     double seconds = dur/1000d; 
     double megabytes = totalbytes/1024/1024; 
     double rate = (megabytes)/seconds; 
     System.out.println("I was equal, even after " + totalbytes 
       + " bytes and reading for " + dur 
       + " ms (" + nf.format(rate) + "MB/sec * 2)" + 
       " with a buffer size of " + myBufferSize/1024 + " kB"); 
    } 
} 
+0

+1. Buen trabajo, Andreas. ¿Podría molestarlo en ejecutarlo con un búfer de 64 MB (sí, megabytes) en la misma máquina y datos? @alamar cree que esto de alguna manera brinda resultados mágicamente excelentes debido a la falta de búsqueda, de lo cual soy escéptico al tener una experiencia del mundo real más cercana a tus propios resultados. –

+1

Mis experimentos también mostraron que los tamaños de buffer 64kb/128kb son óptimos, como en sus pruebas. Para leer un byte [] de 64 kb en un solo paso, no es importante si utilizo BufferedInputStream encima de FileInputStream o no, ellos realizan el mismo. Aunque tuve problemas porque después de que se leyó el archivo una vez, los tiempos se vuelven más pequeños debido al almacenamiento en caché del disco de forma rentable. –

7

Con este tipo de archivos de gran tamaño, que se va a obtener un rendimiento mucho mejor con java.nio.

Además, la lectura de bytes individuales con corrientes de Java puede ser muy lento. El uso una matriz de bytes (2-6K elementos de mis propias experiencias, mmm como parece plataforma/aplicación específica) mejorará dramáticamente su rendimiento de lectura con flujos.

+0

tenía "miedo" de eso. el código es bastante antiguo y funcionó bien desde hace mucho tiempo, pero los archivos tienden a crecer todo el tiempo ... –

+1

Si utiliza un MappedByteBuffer (que usa el subsistema de paginación de memoria virtual del sistema operativo), puede minimizar los cambios en el código y aún obtener mejoras sustanciales en la velocidad. Me arriesgaría a adivinar "órdenes de magnitud" más rápido. –

+1

NIO puede ser confuso, debe comenzar con ByteBuffer.allocateDirect() para obtener el máximo rendimiento (utiliza archivos mapeados en memoria en ese punto). http://java.sun.com/javase/6/docs/api/java/nio/ByteBuffer.html – AgileJon

6

Leer y escribir los archivos con Java puede ser tan rápido. use FileChannels. En cuanto a la comparación de los archivos, obviamente esto tomará mucho tiempo c OMPARANDO byte a byte Aquí hay un ejemplo usando FileChannels y ByteBuffers (podría ser aún más optimizado):

public static boolean compare(String firstPath, String secondPath, final int BUFFER_SIZE) throws IOException { 
    FileChannel firstIn = null, secondIn = null; 
    try { 
     firstIn = new FileInputStream(firstPath).getChannel(); 
     secondIn = new FileInputStream(secondPath).getChannel(); 
     if (firstIn.size() != secondIn.size()) 
      return false; 
     ByteBuffer firstBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE); 
     ByteBuffer secondBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE); 
     int firstRead, secondRead; 
     while (firstIn.position() < firstIn.size()) { 
      firstRead = firstIn.read(firstBuffer); 
      secondRead = secondIn.read(secondBuffer); 
      if (firstRead != secondRead) 
       return false; 
      if (!buffersEqual(firstBuffer, secondBuffer, firstRead)) 
       return false; 
     } 
     return true; 
    } finally { 
     if (firstIn != null) firstIn.close(); 
     if (secondIn != null) firstIn.close(); 
    } 
} 

private static boolean buffersEqual(ByteBuffer first, ByteBuffer second, final int length) { 
    if (first.limit() != second.limit()) 
     return false; 
    if (length > first.limit()) 
     return false; 
    first.rewind(); second.rewind(); 
    for (int i=0; i<length; i++) 
     if (first.get() != second.get()) 
      return false; 
    return true; 
} 
+0

¿Tiene alguna idea acerca de comparar más rápido que el byte por byte? –

+0

Bueno ... Como dije, podrías usar FileChannels (y ByteBuffers). Puedo comparar dos archivos de 1.6GB en 60 segundos. He editado mi publicación original para incluir el código que uso. – laginimaineb

+0

me gusta este ejemplo. no necesita leer los archivos ENTEROS en matrices para compararlos. de lo contrario, tardaría mucho tiempo leyendo archivos que podrían ser los mismos en el byte 1, en lugar de leer 2 bytes. –

0

DMA/SATA son techlonogies/bajo nivel de hardware y no son visibles para cualquier lenguaje de programación que sea.

Para la entrada/salida mapeada en memoria debería usar java.nio, creo.

¿Estás seguro de que no estás leyendo esos archivos por un byte? Eso sería un desperdicio, recomendaría hacerlo bloque por bloque, y cada bloque debería ser algo así como 64 megabytes para minimizar la búsqueda.

+0

te refieres a 64 kb, ¿sí? no megabytes? –

+0

Por qué, megabytes, si puede pagarlo (puede hacerlo hoy). Leer dos archivos por 64 kilobytes no es una buena idea IMO porque el disco buscaría interminablemente. – alamar

+0

Oh, pensó que era un tipo. Esto huele a una optimización prematura para mí. Cuestiono un valor tan grande porque creo que la lectura se bloqueará hasta que se lean los 64 MB completos, lo que resultará en un rendimiento general más lento. Solo las métricas de rendimiento reales se mostrarán de manera concluyente, pero soy muy escéptico con respecto a su teoría. –

2

Puedes echar un vistazo a Suns Article for I/O Tuning (aunque ya un poco anticuado), tal vez puedas encontrar similitudes entre los ejemplos allí y tu código.También eche un vistazo al paquete java.nio que contiene elementos de E/S más rápidos que java.io. El Dr. Dobbs Journal tiene un artículo bastante bueno en high performance IO using java.nio.

Si es así, hay más ejemplos y consejos de ajuste disponibles que deberían poder ayudarle a acelerar su código.

Además, la clase Arrays tiene methods for comparing byte arrays incorporado, quizás estos también se pueden usar para acelerar y aclarar un poco el ciclo.

+0

buena idea con la clase Arrays. Debajo del capó está haciendo una comparación byte-wise en un circuito cerrado. No es tan rápido como el bucle de 32 veces desenrollado que estoy usando actualmente, pero acortaría considerablemente el código, especialmente. para probar el rendimiento de IO. –

5

El siguiente es un buen artículo sobre los méritos relativos de las diferentes formas de leer un archivo en Java. Puede ser de alguna utilidad:

How to read files quickly

+0

¡Gracias a mil millones por el enlace !!!!!!!!!! – Ahamed

1

Para una mejor comparación intente copiar dos archivos a la vez. Un disco duro puede leer un archivo mucho más eficientemente que leer dos (ya que el cabezal tiene que avanzar y retroceder para leer) Una forma de reducir esto es utilizar almacenamientos intermedios más grandes, p. 16 MB. con ByteBuffer.

Con ByteBuffer puede comparar 8 bytes a la vez mediante la comparación de los valores de largo con getLong()

Si su Java es eficiente, la mayor parte de la obra está en el disco/OS para leer y escribir por lo que no deben No es mucho más lento que usar cualquier otro lenguaje (ya que el disco/sistema operativo es el cuello de botella)

No asuma que Java es lento hasta que haya determinado que no es un error en su código.

+0

Pregunto comparar longs ya que tienen que ser construidos sobre la marcha. ¿No debería ser más rápido desenrollar el circuito 8 veces? (o 32 veces eso pareció ser óptimo en mis experimentos). Estoy de acuerdo en que "select no está roto", por lo que IO será rápido, pero sé del pasado (y quizás ya no sea cierto) que Java IO es/fue mucho más lento que decir Pascal/C IO. Pero como la mayoría de las aplicaciones contienen más que IO, ahora Java es aún más rápido en total. –

+0

Para ByteBuffers directos, longs no se construyen en Java. Es solo una llamada a JNI (pueden estar construidos en código C). Estoy bastante seguro de que es más rápido que una llamada por byte. En una respuesta posterior, demuestro que esta función es @ 74.8 MB/s leyendo dos archivos del mismo disco que pueden ser lo suficientemente rápidos. –

+0

@ Peter Lawrey: ¿Has probado esos grandes buffers? Fuiste tú quien [me dijo] (http://stackoverflow.com/a/11610367/581205) que el sistema operativo puede recuperar previamente varios archivos y parece que usa búferes internamente enormes al hacerlo. – maaartinus

6

Después de modificar su función NIO consigo los siguientes resultados se comparan.

I was equal, even after 4294967296 bytes and reading for 304594 ms (13.45MB/sec * 2) with a buffer size of 1024 kB 
I was equal, even after 4294967296 bytes and reading for 225078 ms (18.20MB/sec * 2) with a buffer size of 4096 kB 
I was equal, even after 4294967296 bytes and reading for 221351 ms (18.50MB/sec * 2) with a buffer size of 16384 kB 

Nota: esto significa que los archivos se leen a una velocidad de 37 MB/s

Ejecución del mismo en una unidad más rápida

I was equal, even after 4294967296 bytes and reading for 178087 ms (23.00MB/sec * 2) with a buffer size of 1024 kB 
I was equal, even after 4294967296 bytes and reading for 119084 ms (34.40MB/sec * 2) with a buffer size of 4096 kB 
I was equal, even after 4294967296 bytes and reading for 109549 ms (37.39MB/sec * 2) with a buffer size of 16384 kB 

Nota: esto significa que los archivos están siendo leído a una velocidad de 74.8 MB/s

private static boolean nioBuffersEqual(ByteBuffer first, ByteBuffer second, final int length) { 
    if (first.limit() != second.limit() || length > first.limit()) { 
     return false; 
    } 
    first.rewind(); 
    second.rewind(); 
    int i; 
    for (i = 0; i < length-7; i+=8) { 
     if (first.getLong() != second.getLong()) { 
      return false; 
     } 
    } 
    for (; i < length; i++) { 
     if (first.get() != second.get()) { 
      return false; 
     } 
    } 
    return true; 
} 
-1

Intente configurar el búfer en el flujo de entrada de hasta varios megabytes.

1

Descubrí que muchos de los artículos vinculados en esta publicación están muy desactualizados (también hay algunas cosas muy interesantes). Hay algunos artículos vinculados desde 2001, y la información es cuestionable en el mejor de los casos. Martin Thompson, de simpatía mecánica, escribió bastante sobre esto en 2011. Por favor refiérase a lo que escribió para los antecedentes y la teoría de esto.

He encontrado que NIO o no tiene muy poco que ver con el rendimiento. Se trata mucho más del tamaño de los búferes de salida (lee la matriz de bytes en ese). NIO no es magia, hazlo ir rápido salsa de escala web.

Pude tomar los ejemplos de Martin y usar 1.0Stream OutputStream y hacerlo gritar. NIO también es rápido, pero el indicador más grande es el tamaño del búfer de salida, no importa si usa o no NIO, a menos que, por supuesto, esté usando un NIO mapeado en memoria, entonces sí importa. :)

Si desea obtener información actualizada autorizada sobre esto, ver el blog de Martín:

http://mechanical-sympathy.blogspot.com/2011/12/java-sequential-io-performance.html

Si desea ver cómo NIO no hace que gran parte de la diferencia (ya que era capaz para escribir ejemplos usando IO regular que eran más rápidos) ver esto:

http://www.dzone.com/links/fast_java_io_nio_is_always_faster_than_fileoutput.html

he probado mi suposición en nuevas ventanas portátil con un disco duro rápido, mi MacBook Pro con SSD, una EC2 xlarge, y un EC2 4x grande con IOPS al máximo/E/S de alta velocidad (y pronto en una matriz de disco de fibra NAS de disco grande) para que funcione (hay algunos problemas con él para instancias EC2 más pequeñas, pero si le importa el rendimiento). ¿Vas a usar una pequeña instancia de EC2?). Si usa hardware real, en mis pruebas hasta ahora, IO tradicional siempre gana. Si usa alto/IO EC2, este también es un claro ganador. Si utiliza en instancias EC2 con alimentación, NIO puede ganar.

No hay sustituto para la evaluación comparativa.

De todos modos, no soy un experto, acabo de hacer algunas pruebas empíricas utilizando el marco que Sir Martin Thompson escribió en su blog.

Tomé esta a la siguiente etapa y se utiliza Files.newInputStream (de JDK 7) con TransferQueue para crear una receta para hacer Java grito de E/S (incluso en pequeñas instancias de EC2). La receta se puede encontrar en la parte inferior de esta documentación para Boon (https://github.com/RichardHightower/boon/wiki/Auto-Growable-Byte-Buffer-like-a-ByteBuilder). Esto me permite usar un OutputStream tradicional pero con algo que funciona bien en instancias EC2 más pequeñas. (Soy el autor principal de Boon. Pero estoy aceptando nuevos autores. La paga apesta. 0 $ por hora. Pero la buena noticia es que puedo duplicar su paga siempre que lo desee.)

Mis 2 centavos.

Consulte esto para ver por qué TransferQueue es importante.http://php.sabscape.com/blog/?p=557

aprendizajes clave:

  1. Si se preocupan por el rendimiento nunca, nunca, hasta ahora el uso BufferedOutputStream.
  2. NIO no siempre es igual al rendimiento.
  3. El tamaño del búfer es el más importante.
  4. El almacenamiento de búferes para escritura a alta velocidad es fundamental.
  5. GC puede/hará/implosiona su rendimiento para escrituras de alta velocidad.
  6. Tienes que tener algún mecanismo para volver a utilizar los búferes gastados.
Cuestiones relacionadas