python - tutorial - uso de memoria en la manipulación/procesamiento de estructuras de datos grandes
para que se usa elastic search (6)
Antes de comenzar a rasgarse el pelo sobre el recolector de basura, es posible que pueda evitar ese golpe de 100 mb al cargar todo el archivo en la memoria utilizando un objeto de archivo mapeado en la memoria. Vea el módulo mmap .
Tengo varios archivos grandes (~ 100 Mb) que estoy procesando regularmente. Mientras intento eliminar estructuras de datos innecesarias durante el procesamiento, el consumo de memoria es demasiado alto. Me preguntaba si existe una forma de manipular eficientemente grandes datos, por ejemplo:
def read(self, filename):
fc = read_100_mb_file(filename)
self.process(fc)
def process(self, content):
# do some processing of file content
¿Hay una duplicación de estructuras de datos? ¿No es más eficiente con la memoria el uso de un atributo de toda la clase como self.fc?
¿Cuándo debería usar la recolección de basura? Sé sobre el módulo gc, pero ¿lo llamo después de I del fc
por ejemplo?
actualizar
ps 100 Mb no es un problema en sí mismo. pero la conversión de flotante, el procesamiento posterior agrega significativamente más tanto al conjunto de trabajo como al tamaño virtual (estoy en Windows).
No lea todo el archivo de 100 meg a la vez. Use transmisiones para procesar un poco a la vez. Echa un vistazo a esta publicación de blog que habla sobre el manejo de grandes archivos csv y xml. http://lethain.com/entry/2009/jan/22/handling-very-large-csv-and-xml-files-in-python/
Aquí hay una muestra del código del artículo.
from __future__ import with_statement # for python 2.5
with open(''data.in'',''r'') as fin:
with open(''data.out'',''w'') as fout:
for line in fin:
fout.write('',''.join(line.split('' '')))
Por lo tanto, a partir de sus comentarios, supongo que su archivo se ve más o menos así:
item1,item2,item3,item4,item5,item6,item7,...,itemn
que todos ustedes reducen a un solo valor mediante la aplicación repetida de alguna función de combinación. Como solución, solo lea un solo valor a la vez:
def read_values(f):
buf = []
while True:
c = f.read(1)
if c == ",":
yield parse("".join(buf))
buf = []
elif c == "":
yield parse("".join(buf))
return
else:
buf.append(c)
with open("some_file", "r") as f:
agg = initial
for v in read_values(f):
agg = combine(agg, v)
De esta forma, el consumo de memoria se mantiene constante, a menos que el agg
crezca a tiempo.
- Proporcione implementaciones apropiadas de
initial
,parse
ycombine
- No lea el archivo byte a byte, pero lea en un búfer fijo, analice desde el búfer y lea más cuando lo necesite
Esto es básicamente lo que hace la función de
reduce
interna, pero he usado un ciclo explícito para mayor claridad. Aquí está lo mismo conreduce
:with open("some_file", "r") as f: agg = reduce(combine, read_values(f), initial)
Espero haber interpretado tu problema correctamente.
Antes que nada, no toque el recolector de basura. Ese no es el problema, ni la solución.
Parece que el verdadero problema que tienes no está en la lectura del archivo, sino en las estructuras de datos que estás asignando mientras procesas los archivos. Condering utilizando del para eliminar estructuras que ya no necesita durante el procesamiento. Además, podría considerar utilizar Marshal para descargar algunos de los datos procesados en el disco mientras trabaja en los siguientes 100mb de archivos de entrada.
Para la lectura de archivos, tiene básicamente dos opciones: archivos de estilo Unix como flujos o archivos mapeados en la memoria. Para los archivos basados en secuencias, el objeto de archivo predeterminado de python ya está almacenado en el búfer, por lo que el código más simple también es probablemente el más eficiente:
with open("filename", "r") as f: for line in f: # do something with a line of the files
Alternativamente, puede usar f.read ([size]) para leer los bloques del archivo. Sin embargo, generalmente hace esto para obtener un rendimiento de la CPU, al procesar varias veces la parte de procesamiento de la secuencia de comandos, para que pueda leer y procesar al mismo tiempo. Pero no ayuda con el uso de la memoria; de hecho, usa más memoria.
La otra opción es mmap, que se ve así:
with open("filename", "r+") as f: map = mmap.mmap(f.fileno(), 0) line = map.readline() while line != '''': # process a line line = map.readline()
Esto a veces supera a las transmisiones, pero tampoco mejora el uso de la memoria.
Sugeriría mirar la presentación de David Beazley sobre el uso de generadores en Python. Esta técnica le permite manejar una gran cantidad de datos, y hacer un procesamiento complejo, de forma rápida y sin explotar el uso de su memoria. OMI, el truco es no tener una gran cantidad de datos en la memoria lo más eficientemente posible; el truco es evitar cargar una gran cantidad de datos en la memoria al mismo tiempo.
En su código de ejemplo, los datos se almacenan en la variable fc
. Si no mantiene una referencia a fc
, el contenido de todo el archivo se eliminará de la memoria cuando finalice el método de read
.
Si no lo son, entonces mantendrás una referencia en alguna parte . Tal vez la referencia se está creando en read_100_mb_file
, tal vez en process
. Si no hay referencia, la implementación de CPython lo desasignará casi de inmediato.
Hay algunas herramientas para ayudarlo a encontrar dónde está esta referencia, guppy , dowser , pysizer ...