2010-08-21 14 views
6

¿Cómo se procesan los archivos de datos binarios grandes en Clojure? Supongamos que los datos/archivos son de aproximadamente 50 MB, lo suficientemente pequeños como para procesarse en la memoria (pero no con una implementación ingenua).¿Cómo procesar datos binarios grandes en Clojure?

El siguiente código elimina correctamente^M a partir de archivos pequeños pero lanza OutOfMemoryError para archivos más grandes (como 6MB):

(defn read-bin-file [file] 
    (to-byte-array (as-file file))) 

(defn remove-cr-from-file [file] 
    (let [dirty-bytes (read-bin-file file) 
     clean-bytes (filter #(not (= 13 %)) dirty-bytes) 
     changed? (< (count clean-bytes) (alength dirty-bytes))] ; OutOfMemoryError 
    (if changed? 
     (write-bin-file file clean-bytes)))) ; writing works fine 

Parece que Java matrices de bytes no pueden ser tratados como SEQ ya que es extremadamente ineficiente

Por otro lado, las soluciones con aset, aget y areduce son hinchadas, feas e imperativas porque no se puede usar realmente la biblioteca de secuencias de Clojure.

¿Qué me estoy perdiendo? ¿Cómo se procesan los archivos de datos binarios grandes en Clojure?

Respuesta

6

Probablemente usaré aget/aset/areduce personalmente aquí - pueden ser imprescindibles pero son herramientas útiles cuando se trata de matrices, y no las encuentro particularmente feas. Si desea envolverlos en una función agradable, por supuesto, puede :-)

Si está decidido a utilizar secuencias, su problema será la construcción y recorrido de la secuencia, ya que esto requerirá creación y almacenamiento de un nuevo objeto seq para cada byte en la matriz. Esto es probablemente ~ 24 bytes para cada byte de la matriz ......

Así que el truco es hacer que funcione perezosamente, en cuyo caso los objetos anteriores serán recolectados antes de que llegue al final de la formación. Sin embargo, para que esto funcione, deberá evitar mantener alguna referencia al encabezado de la secuencia cuando recorra la secuencia (por ejemplo, con conteo).

El siguiente podría trabajar (no probado), pero dependerá de escritura-bin-archivo que está siendo implementado de una manera perezosa ambiente:

(defn remove-cr-from-file [file] 
    (let [dirty-bytes (read-bin-file file) 
     clean-bytes (filter #(not (= 13 %)) dirty-bytes) 
     changed-bytes (count (filter #(not (= 13 %)) dirty-bytes)) 
     changed? (< changed-bytes (alength dirty-bytes))] 
    (if changed? 
     (write-bin-file file clean-bytes)))) 

Nota: este es esencialmente el mismo que el código, pero construye una secuencia diferida separada para contar la cantidad de bytes modificados.

+1

Gracias! La pereza hizo el truco como sugirió. Para resumir, procesar archivos binarios en Clojure: ** Usar la pereza es relativamente fácil y tiene una huella de memoria baja, pero es muy ineficiente en la CPU. La clave es nunca darse cuenta de la secuencia completa. ** El uso de loop/recur + aset/aget/areduce/amap es imprescindible, tiene un espacio de memoria bajo Y es mucho más rápido. Esta es probablemente la forma "correcta" de hacerlo. En mi caso particular, uno debería implementar areduce, supongo. – qertoip

Cuestiones relacionadas