ubicacion policies permisos instalacion instala administrador java storage

policies - Realmente fuerza la sincronización/enjuague de archivos en Java



permisos java (5)

(Sé que esta es una respuesta tardía, pero me encontré con este hilo haciendo una búsqueda en Google, y es probable que también termine aquí).

Su llamada a sync () en Java en un único descriptor de archivo, por lo que solo los búferes relacionados con ese archivo se eliminan en el disco.

En C y en la línea de comandos, está llamando a sync () en todo el sistema operativo, por lo que cada búfer de archivo se vacía en el disco, por todo lo que hace su O / S.

Para ser comparable, la llamada C debe ser syncfs (fp);

Desde la página man de Linux:

sync() causes all buffered modifications to file metadata and data to be written to the underlying file systems. syncfs() is like sync(), but synchronizes just the file system contain‐ ing file referred to by the open file descriptor fd.

¿Cómo se pueden borrar / sincronizar los datos escritos en un archivo con el dispositivo de bloque por Java?

Intenté este código con NIO:

FileOutputStream s = new FileOutputStream(filename) Channel c = s.getChannel() while(xyz) c.write(buffer) c.force(true) s.getFD().sync() c.close()

Supuse que c.force (verdadero) junto con s.getFD (). Sync () debería ser suficiente porque el documento para estados de force

Obliga a que cualquier actualización del archivo de este canal se escriba en el dispositivo de almacenamiento que lo contiene. Si el archivo de este canal reside en un dispositivo de almacenamiento local, cuando este método retorne, se garantiza que todos los cambios realizados en el archivo desde que se creó este canal o desde que se invocó este método por última vez se hayan escrito en ese dispositivo. Esto es útil para garantizar que la información crítica no se pierda en el caso de un bloqueo del sistema.

La documentación para sync estados:

Fuerce todos los búferes del sistema para que se sincronicen con el dispositivo subyacente. Este método retorna después de que todos los datos y atributos modificados de este FileDescriptor se hayan escrito en los dispositivos correspondientes. En particular, si este FileDescriptor se refiere a un medio de almacenamiento físico, como un archivo en un sistema de archivos, la sincronización no regresará hasta que todas las copias modificadas en memoria de los búferes asociados con este FileDesecriptor se hayan escrito en el medio físico. la sincronización está destinada a ser utilizada por código que requiere almacenamiento físico (como un archivo) para estar en un estado conocido.

Estas dos llamadas deberían ser suficientes. ¿Lo es? Supongo que no lo son.

Antecedentes: hago una pequeña comparación de rendimiento (2 GB, escritura secuencial) usando C / Java y la versión de Java es dos veces más rápida que la versión C y probablemente más rápida que el hardware (120 MB / s en una sola HD). También traté de ejecutar la sincronización de la herramienta de línea de comandos con Runtime.getRuntime () .exec ("sincronización") pero eso no ha cambiado el comportamiento.

El código C que resulta en 70 MB / s es (usando las API de bajo nivel (abrir, escribir, cerrar) no cambia mucho):

FILE* fp = fopen(filename, "w"); while(xyz) { fwrite(buffer, 1, BLOCK_SIZE, fp); } fflush(fp); fclose(fp); sync();

Sin la llamada final para sincronizar; Obtuve valores poco realistas (más de 1 GB, también conocido como rendimiento de la memoria principal).

¿Por qué hay una gran diferencia entre C y Java? Hay dos posibilidades: no sincronizo los datos correctamente en Java o el código C es subóptimo por alguna razón.

Actualización: He hecho corridas con "strace -cfT cmd". Aquí están los resultados:

C (API de bajo nivel): MB / s 67.389782

% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 87.21 0.200012 200012 1 fdatasync 11.05 0.025345 1 32772 write 1.74 0.004000 4000 1 sync

C (API de alto nivel): MB / s 61.796458

% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 73.19 0.144009 144009 1 sync 26.81 0.052739 1 65539 write

Java (1.6 SUN JRE, java.io API): MB / s 128.6755466197537

% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 80.07 105.387609 3215 32776 write 2.58 3.390060 3201 1059 read 0.62 0.815251 815251 1 fsync

Java (1.6 SUN JRE, java.nio API): MB / s 127.45830221558376

5.52 0.980061 490031 2 fsync 1.60 0.284752 9 32774 write 0.00 0.000000 0 80 close

Los valores de tiempo parecen ser solo del sistema y, por lo tanto, carecen de sentido.

Actualización 2: cambié a otro servidor, reinicié mi funcionamiento y uso un ext3 nuevo formateado. Ahora obtengo solo un 4% de diferencias entre Java y C. Simplemente no sé qué salió mal. A veces las cosas son extrañas. Debería haber intentado la medición con otro sistema antes de escribir esta pregunta. Lo siento.

Actualización 3: Para resumir las respuestas:

  • Utilice c.force (true) seguido de s.getFD (). Sync () para Java NIO y s.flush () y s.getFD (). Sync () para la API de transmisión de Java. Para la API de alto nivel en C, no te olvides de sincronizar. A fflush envió los datos al sistema operativo, pero no trae sus datos al dispositivo de bloque.
  • Usa strace para analizar las llamadas de sys hechas por un comando
  • Revise sus resultados antes de publicar una pregunta.

Actualización 4: tenga en cuenta la siguiente question seguimiento.


El código C podría ser subóptimo, ya que usa stdio en lugar de raw write (). Pero entonces, ¿Java podría ser más óptimo porque asigna buffers más grandes?

De todos modos, solo puedes confiar en el APIDOC. El resto está más allá de tus deberes.


En realidad, en C solo quieres llamar a fsync() en el descriptor de un archivo, no a sync() (o al comando "sync") que indica al kernel que flush todos los buffers del disco en todo el sistema.

Si strace ( strace tienes strace específico para Linux) la JVM deberías poder observar una llamada al sistema fsync() o fdatasync() en tu archivo de salida. Eso sería lo que esperaría del getFD() . llamada de sync() para hacer. Supongo que c.force(true) simplemente c.force(true) a NIO que debe llamarse a fsync() después de cada escritura. ¿Podría ser simplemente que la JVM que está utilizando no implementa la llamada de sync() ?

No estoy seguro de por qué no notó ninguna diferencia al llamar a "sincronización" como un comando: pero, obviamente, después de la primera invocación de sincronización, los siguientes suelen ser bastante más rápidos. Una vez más, me inclinaría a romper strace (truss en Solaris) como un "¿qué está pasando realmente aquí?" herramienta.


Es una buena idea utilizar la finalización de integridad de datos de E / S sincronizada. Sin embargo, su muestra C está utilizando el método incorrecto. Utiliza sync() , que se utiliza para sincronizar todo el sistema operativo.

Si desea escribir los bloques de ese único archivo en el disco, necesita usar fsync(2) o fdatasync(2) en C. A propósito: cuando usa stdio con búfer en C (o un BufferedOutputStream o algún escritor en Java) necesita enjuagar ambos antes de sincronizar.

La variante fdatasync() es un poco más eficiente si el archivo no ha cambiado el nombre o el tamaño desde la sincronización. Pero también puede que no persista todos los metadatos. Si desea escribir sus propios sistemas transaccionales de bases de datos seguras, necesita observar algunas cosas más (como sincronizar el directorio padre).


Necesita decirnos más sobre el hardware y el sistema operativo, también la versión específica de Java. ¿Cómo estás midiendo este rendimiento?

Tiene razón en que forzar / sincronizar debería forzar la salida de los datos a los medios físicos.

Aquí hay una versión sin procesar de la copia. Compilado con gcc 4.0 en una Mac Intel, debe estar limpio.

/* rawcopy -- pure C, system calls only, copy argv[1] to argv[2] */ /* This is a test program which simply copies from file to file using * only system calls (section 2 of the manual.) * * Compile: * * gcc -Wall -DBUFSIZ=1024 -o rawcopy rawcopy.c * * If DIRTY is defined, then errors are interpreted with perror(3). * This is ifdef''d so that the CLEAN version is free of stdio. For * convenience I''m using BUFSIZ from stdio.h; to compile CLEAN just * use the value from your stdio.h in place of 1024 above. * * Compile DIRTY: * * gcc -DDIRTY -Wall -o rawcopy rawcopy.c * */ #include <fcntl.h> #include <sys/types.h> #include <sys/uio.h> #include <stdlib.h> #include <unistd.h> #if defined(DIRTY) # if defined(BUFSIZ) # error "Don''t define your own BUFSIZ when DIRTY" # endif # include <stdio.h> # define PERROR perror(argv[0]) #else # define CLEAN # define PERROR # if ! defined(BUFSIZ) # error "You must define your own BUFSIZ with -DBUFSIZ=<number>" # endif #endif char * buffer[BUFSIZ]; /* by definition stdio BUFSIZ should be optimal size for read/write */ extern int errno ; /* I/O errors */ int main(int argc, char * argv[]) { int fdi, fdo ; /* Input/output file descriptors */ ssize_t len ; /* length to read/write */ if(argc != 3){ PERROR; exit(errno); } /* Open the files, returning perror errno as the exit value if fails. */ if((fdi = open(argv[1],O_RDONLY)) == -1){ PERROR; exit(errno); } if((fdo = open(argv[2], O_WRONLY|O_CREAT)) == -1){ PERROR; exit(errno); } /* copy BUFSIZ bytes (or total read on last block) fast as you can. */ while((len = read(fdi, (void *) buffer, BUFSIZ)) > -1){ if(len == -1){ PERROR; exit(errno); } if(write(fdo, (void*)buffer, len) == -1){ PERROR; exit(errno); } } /* close and fsync the files */ if(fsync(fdo) ==-1){ PERROR; exit(errno); } if(close(fdo) == -1){ PERROR; exit(errno); } if(close(fdi) == -1){ PERROR; exit(errno); } /* if it survived to here, all worked. */ exit(0); }