Estoy tratando de averiguar si hay alguna diferencia en el rendimiento (o ventajas) cuando usamos nio FileChannel
versus normal FileInputStream/FileOuputStream
para leer y escribir archivos en el sistema de archivos. Observé que en mi máquina ambos funcionan al mismo nivel, también muchas veces el FileChannel
camino es más lento. ¿Puedo saber más detalles comparando estos dos métodos? Aquí está el código que usé, el archivo con el que estoy probando está disponible 350MB
. ¿Es una buena opción usar clases basadas en NIO para E / S de archivos, si no estoy buscando 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");
}
}
java
optimization
file
nio
operations
Keshav
fuente
fuente
transferTo
/transferFrom
sería más convencional para copiar archivos. Cualquiera que sea la técnica no debería hacer que su disco duro sea más rápido o más lento, aunque supongo que podría haber un problema si lee pequeños fragmentos a la vez y hace que la cabeza pase una cantidad excesiva de tiempo buscando.Respuestas:
Mi experiencia con archivos de mayor tamaño ha sido
java.nio
más rápida quejava.io
. Sólidamente más rápido. Como en el rango> 250%. Dicho esto, estoy eliminando cuellos de botella obvios, lo que sugiero que podría sufrir su micro-punto de referencia. Áreas potenciales para investigar:El tamaño del búfer. El algoritmo que básicamente tienes es
Mi propia experiencia ha sido que este tamaño de búfer está maduro para el ajuste. Me decidí por 4KB para una parte de mi aplicación, 256 KB para otra. Sospecho que su código está sufriendo con un búfer tan grande. Ejecute algunos puntos de referencia con buffers de 1 KB, 2 KB, 4KB, 8 KB, 16 KB, 32 KB y 64 KB para probarlo usted mismo.
No realice pruebas de referencia de Java que lean y escriban en el mismo disco.
Si lo hace, entonces realmente está comparando el disco, y no Java. También sugeriría que si su CPU no está ocupada, entonces probablemente esté experimentando algún otro cuello de botella.
No use un búfer si no lo necesita.
¿Por qué copiar a la memoria si su objetivo es otro disco o una NIC? Con archivos más grandes, la latencia incurada no es trivial.
Como otros han dicho, use
FileChannel.transferTo()
oFileChannel.transferFrom()
. La ventaja clave aquí es que la JVM usa el acceso del sistema operativo a DMA ( Acceso directo a memoria ), si está presente. (Esto depende de la implementación, pero las versiones modernas de Sun e IBM en CPU de uso general son buenas para usar). Lo que sucede es que los datos van directamente a / desde el disco, al bus y luego al destino ... sin pasar por ningún circuito RAM o la CPU.La aplicación web en la que pasé mis días y mi noche trabajando es muy pesada. También hice micro benchmarks y benchmarks del mundo real. Y los resultados están en mi blog, eche un vistazo:
Utilice 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 exactamente lo que planea hacer, con la carga que espera, en el hardware que espera.
Mis puntos de referencia son sólidos y confiables porque tuvieron lugar en un sistema de producción, un sistema robusto, un sistema bajo carga, reunidos en registros. No es el disco SATA de 7200 RPM de 2.5 "mientras miraba intensamente mientras la JVM trabajaba mi disco duro.
¿En qué estás corriendo? Importa.
fuente
Si lo que desea comparar es el rendimiento de la copia de archivos, entonces, para la prueba del canal, debe hacer esto en su lugar:
Esto no será más lento que almacenarse en un búfer de un canal a otro, y potencialmente será enormemente más rápido. De acuerdo con los Javadocs:
fuente
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 volteo del buffer de datos NIO siempre supera al IO estándar.
Copiando 1000x2MB
Copiando 100x20mb
Copiando 1x1000mb
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?
fuente
Respondiendo la parte de "utilidad" de la pregunta:
Un inconveniente bastante sutil de usar
FileChannel
overFileOutputStream
es que realizar cualquiera de sus operaciones de bloqueo (por ejemplo,read()
owrite()
) desde un hilo que está en estado interrumpido hará que el canal se cierre abruptamentejava.nio.channels.ClosedByInterruptException
.Ahora, esto podría ser algo bueno si lo que sea que
FileChannel
se haya utilizado es parte de la función principal del hilo, y el diseño lo tuvo en cuenta.Pero también podría ser molesto si lo utiliza alguna característica 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 lamentable que esto sea tan sutil porque no tener en cuenta esto puede provocar errores que afectan la integridad de la escritura. [1] [2]
fuente
Probé el rendimiento de FileInputStream vs. FileChannel para decodificar archivos codificados en base64. En mis experimentos probé un archivo bastante grande y el io tradicional siempre fue un poco más rápido que el 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 los jvm modernos son bastante buenos para eliminar bloqueos innecesarios.
fuente
Si no está utilizando la función transferTo o las funciones sin bloqueo, no notará una diferencia entre IO tradicional y NIO (2) porque el IO tradicional se asigna a NIO.
Pero si puede usar las funciones de NIO como transferFrom / To o desea usar Buffers, entonces, por supuesto, NIO es el camino a seguir.
fuente
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.
fuente
java.nio
es más rápido con archivos más grandes quejava.io
, no más pequeños.java.nio
es rápido siempre y cuando el archivo sea lo suficientemente pequeño como para mapearse en la memoria. Si se hace más grande (200 MB y más)java.io
es más rápido.FileChannel.read()
. No hay un solo enfoque para leer archivos usandojava.nio
.