Pregunta

Estoy tratando de averiguar si hay alguna diferencia en el rendimiento (o ventajas) cuando usamos nio FileChannel en lugar de FileInputStream / FileOuputStream normal para leer y escribir archivos en sistema de archivos. Observé que en mi máquina ambos funcionan al mismo nivel, y también muchas veces la forma en FileChannel es más lenta. ¿Puedo saber más detalles comparando estos dos métodos? Aquí está el código que utilicé, el archivo que estoy probando está alrededor de 350MB . ¿Es una buena opción usar clases basadas en NIO para la E / S de archivos, si no estoy viendo el acceso aleatorio u otras características avanzadas?

package trialjavaprograms;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class JavaNIOTest {
    public static void main(String[] args) throws Exception {
        useNormalIO();
        useFileChannel();
    }

    private static void useNormalIO() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        InputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        byte[] buf = new byte[64 * 1024];
        int len = 0;
        while((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fos.flush();
        fos.close();
        is.close();
        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }

    private static void useFileChannel() throws Exception {
        File file = new File("/home/developer/test.iso");
        File oFile = new File("/home/developer/test2");

        long time1 = System.currentTimeMillis();
        FileInputStream is = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(oFile);
        FileChannel f = is.getChannel();
        FileChannel f2 = fos.getChannel();

        ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
        long len = 0;
        while((len = f.read(buf)) != -1) {
            buf.flip();
            f2.write(buf);
            buf.clear();
        }

        f2.close();
        f.close();

        long time2 = System.currentTimeMillis();
        System.out.println("Time taken: "+(time2-time1)+" ms");
    }
}
¿Fue útil?

Solución

Mi experiencia con archivos de mayor tamaño ha sido que java.nio es más rápido que java.io . Sólidamente más rápido. Me gusta en el rango > 250%. Dicho esto, estoy eliminando los cuellos de botella evidentes, que sugiero que su micro-punto de referencia podría sufrir. Áreas potenciales para la investigación:

El tamaño del búfer. El algoritmo que tienes básicamente es

  • copiar del disco al búfer
  • copiar desde el búfer al disco

Mi propia experiencia ha sido que este tamaño de búfer es ripe para la optimización. Me he conformado con 4KB para una parte de mi aplicación, 256KB para otra. Sospecho que su código está sufriendo con un búfer tan grande. Ejecute algunos puntos de referencia con búferes de 1 KB, 2 KB, 4 KB, 8 KB, 16 KB, 32 KB y 64 KB para demostrarlo.

No realice pruebas de java que lean y escriban en el mismo disco.

Si lo hace, entonces realmente está haciendo una evaluación comparativa del disco, y no de Java. También sugeriría que si su CPU no está ocupada, es probable que esté experimentando algún otro cuello de botella.

No uses un búfer si no lo necesitas.

¿Por qué copiar en la memoria si su destino es otro disco o una NIC? Con archivos más grandes, la latencia incurrida es no trivial.

Como han dicho otros, use FileChannel.transferTo () o FileChannel.transferFrom () . La ventaja clave aquí es que la JVM utiliza el acceso del SO a DMA ( Métricas de rendimiento en el mundo real: java.io vs. java.nio

  • Métricas de rendimiento en el mundo real: java.io vs. java.nio (The Sequel)
  • Utilizar datos y entornos de producción

    Los micro-puntos de referencia son propensos a la distorsión. Si puede, haga el esfuerzo de recopilar datos de lo que planea hacer, con la carga que espera, en el hardware que espera.

    Mis puntos de referencia son sólidos y confiables porque se realizaron en un sistema de producción, un sistema robusto, un sistema bajo carga, reunidos en registros. No de mi portátil de 7200 RPM 2.5 " La unidad SATA mientras miraba intensamente mientras la JVM trabajaba en mi disco duro.

    ¿En qué estás corriendo? Importa.

    Otros consejos

    Si lo que quieres comparar es el rendimiento de la copia de archivos, entonces para la prueba de canal debes hacer esto en su lugar:

    final FileInputStream inputStream = new FileInputStream(src);
    final FileOutputStream outputStream = new FileOutputStream(dest);
    final FileChannel inChannel = inputStream.getChannel();
    final FileChannel outChannel = outputStream.getChannel();
    inChannel.transferTo(0, inChannel.size(), outChannel);
    inChannel.close();
    outChannel.close();
    inputStream.close();
    outputStream.close();
    

    Esto no será más lento que el búfer de un canal a otro, y potencialmente será mucho más rápido. Según los Javadocs:

      

    Muchos sistemas operativos pueden transferir bytes directamente desde el caché del sistema de archivos al canal de destino sin copiarlos realmente.

    Basado en mis pruebas (Win7 64bit, 6GB RAM, Java6), NIO transferFrom es rápido solo con archivos pequeños y se vuelve muy lento en archivos más grandes. El volcado de la base de datos NIO siempre supera a la IO estándar.

    • Copiando 1000x2MB

      1. NIO (transferFrom) ~ 2300ms
      2. NIO (archivo de datos directo 5000b flip) ~ 3500ms
      3. IO estándar (buffer 5000b) ~ 6000ms
    • Copiando 100x20mb

      1. NIO (inversión de datos directa 5000b flip) ~ 4000ms
      2. NIO (transferFrom) ~ 5000ms
      3. IO estándar (buffer 5000b) ~ 6500ms
    • Copiando 1x1000mb

      1. NIO (archivo de datos directo 5000b flip) ~ 4500s
      2. IO estándar (buffer 5000b) ~ 7000 ms
      3. NIO (transferFrom) ~ 8000ms

    El método transferTo () funciona en fragmentos de un archivo; no fue pensado como un método de copia de archivos de alto nivel: ¿Cómo copiar un archivo grande en Windows XP?

    Respondiendo a la " utilidad " parte de la pregunta:

    Un error bastante sutil de usar FileChannel sobre FileOutputStream es que realiza cualquiera de sus operaciones de bloqueo (por ejemplo, read () o write () ) desde un hilo que está en estado interrumpido hará que el canal se cierre abruptamente con java.nio.channels.ClosedByInterruptException .

    Ahora, esto podría ser bueno si cualquier cosa que se usara para FileChannel es parte de la función principal del hilo, y el diseño tuvo esto en cuenta.

    Pero también podría ser molesto si lo utiliza alguna función auxiliar, como una función de registro. Por ejemplo, puede encontrar que su salida de registro se cierra repentinamente si la función de registro es llamada por un hilo que también se interrumpe.

    Es desafortunado que esto sea tan sutil porque no tener en cuenta esto puede dar lugar a errores que afectan la integridad de la escritura. [1] [2]

    Probé el rendimiento de FileInputStream frente a FileChannel para descodificar archivos codificados en base64. En mis experiencias, probé archivos bastante grandes y el io tradicional siempre fue un poco más rápido que nio.

    FileChannel podría haber tenido una ventaja en versiones anteriores de jvm debido a la sobrecarga de sincronización en varias clases relacionadas con io, pero jvm moderno es bastante bueno para eliminar bloqueos innecesarios.

    Si no está utilizando la función transferTo o las funciones de no bloqueo, no notará una diferencia entre IO tradicional y NIO (2) porque el IO tradicional se asigna a NIO.

    Pero si puedes usar las funciones de NIO como transferFrom / To o si quieres usar Buffers, entonces, por supuesto, NIO es el camino a seguir.

    Mi experiencia es que NIO es mucho más rápido con archivos pequeños. Pero cuando se trata de archivos grandes, FileInputStream / FileOutputStream es mucho más rápido.

    Licenciado bajo: CC-BY-SA con atribución
    No afiliado a StackOverflow
    scroll top