2011-04-10 33 views
19

RandomAccessFile es bastante lento para el acceso aleatorio a un archivo. A menudo lee acerca de la implementación de una capa almacenada en el búfer, pero no es posible encontrar el código en línea.Buffered RandomAccessFile java

Así que mi pregunta es: ¿ustedes chicos que conocen alguna implementación de código abierto de esta clase comparten un puntero o comparten su propia implementación?

Sería bueno que esta pregunta se convirtiera en una colección de enlaces y códigos útiles sobre este problema, que estoy seguro es compartida por muchos y que SUN nunca ha abordado adecuadamente.

Por favor, no hay referencia a MemoryMapping, ya que los archivos pueden ser mucho más grandes que Integer.MAX_VALUE.

+1

Déjame ver si entiendo, quiere decir que java.nio.MemoryByteBuffer no es suficiente, ya que sólo puede contener En teger.MAX_VALUE bytes. ¿Es eso así? –

+0

Eso es alrededor de 2 gigabytes de memoria en un búfer. ¿Qué tan grande es su archivo y cuánta memoria tiene disponible? –

+1

¿Qué/cómo quieres almacenar? Por lo general, está almacenando en búfer una secuencia, pero si desea acceder a un punto arbitrario en un archivo de múltiples conciertos, ¿qué datos exactamente desea almacenar? Supongo que la respuesta a eso le dará su solución (por ejemplo, "siempre quiero precargar 1K de datos después del punto aleatorio). –

Respuesta

12

Bueno, no veo una razón para no usar java.nio.MappedByteBuffer incluso si los archivos son más grandes que Integer.MAX_VALUE.

Evidentemente, no se le permitirá definir un solo MappedByteBuffer para todo el archivo. Pero podría tener varios MappedByteBuffers accediendo a diferentes regiones del archivo.

La definición de la posición y el tamaño de FileChannenel.map es de tipo long, lo que implica que puede proporcionar valores más Integer.MAX_VALUE, lo único que tiene que cuidar es que el tamaño de la memoria intermedia no lo hará ser más grande que Integer.MAX_VALUE.

Por lo tanto, se podría definir varios mapas de la siguiente manera:

buffer[0] = fileChannel.map(FileChannel.MapMode.READ_WRITE,0,2147483647L); 
buffer[1] = fileChannel.map(FileChannel.MapMode.READ_WRITE,2147483647L, Integer.MAX_VALUE); 
buffer[2] = fileChannel.map(FileChannel.MapMode.READ_WRITE, 4294967294L, Integer.MAX_VALUE); 
... 

En resumen, el tamaño no puede ser más grande que Integer.MAX_VALUE, pero la posición de inicio puede estar en cualquier lugar en su archivo.

En el libro Java NIO, el autor Ron Hitchens afirma:

Acceso a un archivo a través del mecanismo mapeo de memoria puede ser mucho más eficiente que la lectura o escritura de datos por medios convencionales, incluso cuando usando canales. No es necesario realizar llamadas explícitas del sistema , que pueden ser que consumen mucho tiempo. Más importante aún, el sistema de memoria virtual del sistema operativo guarda automáticamente en memoria caché las páginas de memoria . Estas páginas se almacenarán en la memoria caché usando la memoria del sistema y no serán las que consuman espacio de la memoria de la JVM montón.

Una vez que una página de memoria se ha hecho válida (traídos de disco), que puede ser visitada de nuevo a velocidad completa por hardware sin la necesidad de realizar otra llamada sistema para obtener los datos. Grandes, archivos estructurados que contienen índices u otras secciones a las que se hace referencia o se actualizan frecuentemente pueden beneficiarse tremendamente de la asignación de memoria. Cuando combinado con bloqueo de archivos para proteger secciones críticas y control transaccional atomicity, comienza a ver cómo los buffers mapeados en la memoria pueden ser poner un buen uso.

Realmente dudo que encuentre una API de terceros que haga algo mejor que eso. Quizás pueda encontrar una API escrita sobre esta arquitectura para simplificar el trabajo.

¿No crees que este enfoque debería funcionar para ti?

+1

Buen enfoque, pero debe tener búferes superpuestos para que pueda leer los registros que están en un límite 2G. – Anon

+0

que es una posible solución e iba a hacer en otra pregunta. una forma eficiente de ajustar múltiples bytebuffers mapeados para archivos grandes. Aquí estaba más buscando un enfoque de búfer, algo así como https://github.com/apache/cassandra/blob/trunk/src/java/org/apache/cassandra/io/util/BufferedRandomAccessFile.java o http: // minddumped.blogspot.com/2009/01/buffered-javaiorandomaccessfile.html – marcorossi

1

Si está ejecutando en una máquina de 64 bits, entonces los archivos mapeados en memoria son su mejor enfoque. Simplemente asigne todo el archivo en una matriz de búferes de igual tamaño, luego elija un búfer para cada registro según sea necesario (es decir, respuesta de edalorzo, aunque quiera superponer búferes para que no tenga registros que abarquen límites) .

Si está ejecutando en una JVM de 32 bits, entonces está atrapado con RandomAccessFile. Sin embargo, puede usarlo para leer un byte[] que contenga su registro completo, luego use un ByteBuffer para recuperar valores individuales de esa matriz. En el peor de los casos, deberá hacer dos accesos a los archivos: uno para recuperar la posición/tamaño del registro y otro para recuperar el registro.

Sin embargo, tenga en cuenta que puede comenzar a estresar al recolector de basura si crea muchos byte[] s, y permanecerá vinculado a IO si rebota en todo el archivo.

+0

@Anon Ciertamente no soy un experto en el tema y por lo tanto, me siento realmente intrigado sobre por qué dices que si se trata de archivos mapeados en memoria de máquina de 64 bits son el mejor enfoque. ¿Lo dice debido a las limitaciones de direccionamiento de memoria de una arquitectura de hardware de 32 bits o por algún otro motivo en particular? –

+1

@edalorzo - se debe a las limitaciones del hardware de 32 bits. En una máquina de 64 bits, su espacio de direcciones virtuales es lo suficientemente grande como para mapear todo el archivo. En una máquina de 32 bits, tendrías que reasignar constantemente pociones del archivo, y es posible que te topes con problemas de GC (los archivos mapeados no son asignados por el recolector de basura, lo que * debería * anular el mapeo de un archivo para que tengas espacio para mapear otro , pero puede hacer una colección completa mientras lo hace). – Anon

+0

sí, estaba buscando exactamente algo así como su solución de 32 bits. mira mi comentario a edalorzo. el primero es una especie de problema. El mapeo en mm de muchas ubicaciones diferentes para lecturas pequeñas (en comparación con el tamaño y el costo de mapeo) no tiene mucho sentido. – marcorossi

2

RandomAccessFile es bastante lento para el acceso aleatorio a un archivo. A menudo lee acerca de la implementación de una capa almacenada en el búfer, pero no es posible encontrar el código en línea.

Bueno, es posible encontrarlo en línea.
Por un lado, el código fuente JAI en JPEG 2000 tiene una aplicación, así como un impl gravado no aun más en: http://www.unidata.ucar.edu/software/netcdf-java/

javadocs:

http://www.unidata.ucar.edu/software/thredds/v4.3/netcdf-java/v4.0/javadoc/ucar/unidata/io/RandomAccessFile.html

+2

si sus archivos están en el rango de GB, seguramente notará una aceleración con los archivos mapeados en la memoria. la aplicación de RandomAccessFile almacenada en el búfer que mencioné es excelente para archivos pequeños y también para requisitos de memoria baja. Los archivos mapeados en memoria ocupan gran cantidad de RAM para hacer su hechicería. – javatothebone

+0

con el único problema que tengo que depender de una biblioteca entera para una clase. ese es el problema. aún así, gracias por los enlaces. – marcorossi

8

Se puede hacer una BufferedInputStream de un RandomAccessFile con código como,

RandomAccessFile raf = ... 
FileInputStream fis = new FileInputStream(raf.getFD()); 
BufferedInputStream bis = new BufferedInputStream(fis); 

Algunas cosas para tomar nota

  1. Cerrando el FileInputStream se cerrará la RandomAccessFile y viceversa
  2. El punto RandomAccessFile y FileInputStream a la misma posición, por lo que la lectura de la FileInputStream avanzará el puntero de archivo para el RandomAccessFile, y viceversa

Probablemente la forma que desea utilizar esto sería algo así como,

RandomAccessFile raf = ... 
FileInputStream fis = new FileInputStream(raf.getFD()); 
BufferedInputStream bis = new BufferedInputStream(fis); 

//do some reads with buffer 
bis.read(...); 
bis.read(...); 

//seek to a a different section of the file, so discard the previous buffer 
raf.seek(...); 
bis = new BufferedInputStream(fis); 
bis.read(...); 
bis.read(...); 
+4

Tomé un enfoque similar, usando el método 'getFD'. Pero en lugar de construir un BufferedInputStream, construí un FileReader y luego un BufferedReader. Eso me da acceso a un método 'readLine' que es más rápido (¿y quizás más compatible con UTF?) Que el proporcionado por RandomAccessFile. –

+0

@ JeffTerrellPh.D. ¿Podrías mostrarnos lo que hiciste? –

Cuestiones relacionadas