2009-07-24 18 views
7

Tengo una serie de scripts que actualmente se leen en una gran cantidad de datos de algunos archivos .CSV. Para mayor eficiencia, uso el módulo Text::CSV_XS para leerlos y luego crear un hash usando una de las columnas como índice. Sin embargo, tengo un lote de archivos y son bastante grandes. Y cada uno de los scripts necesita leer nuevamente los datos.¿Cómo puedo acceder a los datos en muchos archivos CSV grandes rápidamente desde Perl?

La pregunta es: ¿cómo puedo tener un almacenamiento persistente de estos hashes de Perl para que todos puedan leerse nuevamente con un mínimo de CPU?

La combinación de los scripts no es una opción. Me gustaría ...

me aplicó la segunda regla de optimización y perfiles utilizados para encontrar que la gran mayoría de la CPU (alrededor del 90%) fue en:

Text::CSV_XS::fields 
Text::CSV_XS::Parse 
Text::CSV_XS::parse 

Por lo tanto, hice un script de prueba que de lectura en todos los archivos CSV (Texto :: CSV_XS), desechó utilizando el módulo almacenable, y luego fue atrás y leer de nuevo en el uso del módulo de almacenable. Me perfilado esto por lo que pude ver los tiempos de CPU:

$ c:/perl/bin/dprofpp.bat 
Total Elapsed Time = 1809.397 Seconds 
    User+System Time = 950.5560 Seconds 
Exclusive Times 
%Time ExclSec CumulS #Calls sec/call Csec/c Name 
25.6 243.6 243.66 126 1.9338 1.9338 Storable::pretrieve 
20.5 194.9 194.92 893448 0.0002 0.0002 Text::CSV_XS::fields 
9.49 90.19 90.198 893448 0.0001 0.0001 Text::CSV_XS::Parse 
7.48 71.07 71.072 126 0.5641 0.5641 Storable::pstore 
4.45 42.32 132.52 893448 0.0000 0.0001 Text::CSV_XS::parse 
(the rest was in terms of 0.07% or less and can be ignored) 

Por lo tanto, el uso de almacenable cuesta alrededor de 25,6% a cargar de nuevo en comparación con Texto :: CSV_XS a aproximadamente 35%. No hay muchos ahorros ...

¿Alguien ha recibido alguna sugerencia sobre cómo puedo leer estos datos de manera más eficiente?

Gracias por su ayuda.

Respuesta

9

Analice los datos una vez y colóquelos en SQLite db. Consulta utilizando DBI.

+0

Atentamente y friedo me quita el pulgar. – Axeman

+0

@Axeman Gracias. –

+0

Ese es el camino a seguir si no necesita acceso de escritura. –

2

Es muy preferible no extraer toda la lista en la memoria cada vez que ejecuta el script. Usar una base de datos en disco te permitirá hacer esto. Si, por alguna razón, tiene que tocar cada entrada en el archivo CSV cada vez que ejecuta, podría recomendar almacenarlo en un disco RAM en lugar de en un disco físico. Obviamente encaja en la memoria, no creo que obtenga una gran mejora al cambiar el formato en disco en el que lo almacena. La única manera de acelerarlo realmente es almacenarlo en un medio más rápido.

11

La manera más fácil de poner un hash muy grande en el disco, en mi humilde opinión, es con BerkeleyDB. Es rápido, probado en el tiempo y sólido como una roca, y el módulo CPAN proporciona una API atada. Eso significa que puede continuar utilizando su hash como si fuera una estructura de datos en memoria, pero leerá y escribirá automáticamente a través de BerkeleyDB en el disco.

1

Si solo necesita acceder a parte de los datos en cada script, en lugar de TODO, DBM::Deep es probablemente su mejor opción.

Es probable que Disk/IO sea su cuello de botella más grande sin importar lo que haga. Tal vez podrías usar un proveedor de datos que mantenga todos los datos disponibles en un caché mmapped, usando algo como Sys::Mmap::Simple. Nunca he necesitado hacer este tipo de cosas, así que no tengo mucho más que ofrecer.

+0

Explique * por qué * DBM :: Deep sería mejor para acceder a parte de los datos, por favor? – ysth

+1

DBM :: Deep es un hermoso módulo: Piense en almacenar literalmente las estructuras de datos de Perl en el disco sin la necesidad de deserializar la base * completa * como con Storable. Dicho esto, es muy, muy lento si necesita una fracción significativa de los datos. Pone la comodidad sobre el rendimiento. – tsee

3

Bueno, he tomado la sugerencia de Sinan Ünür (gracias!) E hizo una base de datos SQLite y volver a ejecutar mi programa de prueba para comparar conseguir los datos a través de archivos CSV en comparación a obtener los datos de la base de datos SQLite:

$ c:/perl/bin/dprofpp.bat 
Total Elapsed Time = 1705.947 Seconds 
    User+System Time = 1084.296 Seconds 
Exclusive Times 
%Time ExclSec CumulS #Calls sec/call Csec/c Name 
19.5 212.2 212.26 893448 0.0002 0.0002 Text::CSV_XS::fields 
15.7 170.7 224.45 126 1.3549 1.7814 DBD::_::st::fetchall_hashref 
9.14 99.15 99.157 893448 0.0001 0.0001 Text::CSV_XS::Parse 
6.03 65.34 164.49 893448 0.0001 0.0002 Text::CSV_XS::parse 
4.93 53.41 53.412 893574 0.0001 0.0001 DBI::st::fetch 
    [ *removed the items of less than 0.01 percent* ] 

El total de CSV_XS es ​​34,67% en comparación a 20.63% para SQLite que es algo mejor que la solución almacenable que probé antes. Sin embargo, esta no es una comparación justa ya que con la solución CSV_XS tengo que cargar el archivo completo CSV pero con la interfaz SQLite, puedo cargar las piezas que quiero. Por lo tanto, en la práctica, espero que haya una mejoría aún mayor de lo que muestra esta prueba simple.

No he intentado usar BerkeleyDB (lo siento, friedo) en lugar de SQLite, sobre todo porque no vi esa sugerencia hasta que estuve bien involucrado en probar SQLite. Configurar la prueba no fue una tarea trivial ya que casi nunca tuve ocasión de usar bases de datos SQL.

Aún así, la solución es claramente cargar todos los datos en una base de datos y acceder a ellos a través del módulo DBI. Gracias por la ayuda de todos. Todas las respuestas son muy apreciadas.

+0

@Harold Gracias por aceptar mi respuesta, pero, lo más importante, muchas gracias por un buen resumen con los números reales. –

Cuestiones relacionadas