2010-11-26 21 views
20

¿Hay un límite en la memoria para python? He estado usando un script de python para calcular los valores promedio de un archivo que tiene un mínimo de 150 mb de tamaño.Límite de memoria superior?

Dependiendo del tamaño del archivo, a veces encuentro un MemoryError.

¿Se puede asignar más memoria a la python para que no encuentre el error?


EDIT: Código ahora por debajo

NOTA: Los tamaños de los archivos puede variar en gran medida (hasta 20 GB) el tamaño mínimo del archivo es de 150 MB

file_A1_B1 = open("A1_B1_100000.txt", "r") 
file_A2_B2 = open("A2_B2_100000.txt", "r") 
file_A1_B2 = open("A1_B2_100000.txt", "r") 
file_A2_B1 = open("A2_B1_100000.txt", "r") 
file_write = open ("average_generations.txt", "w") 
mutation_average = open("mutation_average", "w") 

files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1] 

for u in files: 
    line = u.readlines() 
    list_of_lines = [] 
    for i in line: 
     values = i.split('\t') 
     list_of_lines.append(values) 

    count = 0 
    for j in list_of_lines: 
     count +=1 

    for k in range(0,count): 
     list_of_lines[k].remove('\n') 

    length = len(list_of_lines[0]) 
    print_counter = 4 

    for o in range(0,length): 
     total = 0 
     for p in range(0,count): 
      number = float(list_of_lines[p][o]) 
      total = total + number 
     average = total/count 
     print average 
     if print_counter == 4: 
      file_write.write(str(average)+'\n') 
      print_counter = 0 
     print_counter +=1 
file_write.write('\n') 
+2

¿Nos puede mostrar su secuencia de comandos? He procesado archivos mucho más grandes en Python sin problemas. – robert

+1

¿Qué intenta hacer tu secuencia de comandos? Me parece que desea calcular el valor promedio de cada cuarta columna en cada uno de los archivos de entrada. ¿Está bien? –

+0

He notado diferencias de rendimiento significativas con respecto a la memoria cuando ejecuto la misma aplicación de Python en Windows (XP) y OS X/Linux. El rendimiento en el lado de Windows tiende a ser el peor. –

Respuesta

25

(Esta es mi tercera respuesta, porque no he entendido lo que el código estaba haciendo en mi original, y luego hizo un pequeño pero crucial error en . mi segunda es de esperar de tres un encanto

ediciones:. Dado que esto parece ser una respuesta popular, he hecho algunas modificaciones para mejorar su aplicación en los últimos años, la mayoría no demasiado importante esto es así si la gente nos Como plantilla, proporcionará una base aún mejor.

Como otros han señalado, el problema MemoryError es más probable porque usted está tratando de leer todo el contenido de archivos de gran tamaño en la memoria y, además de eso, duplicando la cantidad de memoria que necesita la creación de una lista de listas de los valores de cadena de cada línea.

Los límites de memoria de Python están determinados por la cantidad de memoria física y espacio de disco de memoria virtual que su computadora y sistema operativo tienen disponible. Incluso si no lo usa todo y su programa "funciona", usarlo puede ser poco práctico porque lleva demasiado tiempo.

De todos modos, la forma más obvia de evitar eso es procesar cada archivo una sola línea a la vez, lo que significa que tiene que hacer el procesamiento de forma incremental.

Para lograr esto, se mantiene una lista de totales acumulados para cada uno de los campos. Cuando eso finalice, el valor promedio de cada campo se puede calcular dividiendo el valor total correspondiente por el recuento de líneas totales leídas.Una vez hecho esto, estos promedios se pueden imprimir y algunos escritos en uno de los archivos de salida. También hice un esfuerzo consciente para usar nombres de variables muy descriptivos para tratar de hacerlo comprensible.

try: 
    from itertools import izip_longest 
except ImportError: # Python 3 
    from itertools import zip_longest as izip_longest 

GROUP_SIZE = 4 
input_file_names = ["A1_B1_100000.txt", "A2_B2_100000.txt", "A1_B2_100000.txt", 
        "A2_B1_100000.txt"] 
file_write = open("average_generations.txt", 'w') 
mutation_average = open("mutation_average", 'w') # left in, but nothing written 

for file_name in input_file_names: 
    with open(file_name, 'r') as input_file: 
     print('processing file: {}'.format(file_name)) 

     totals = [] 
     for count, fields in enumerate((line.split('\t') for line in input_file), 1): 
      totals = [sum(values) for values in 
         izip_longest(totals, map(float, fields), fillvalue=0)] 
     averages = [total/count for total in totals] 

     for print_counter, average in enumerate(averages): 
      print(' {:9.4f}'.format(average)) 
      if print_counter % GROUP_SIZE == 0: 
       file_write.write(str(average)+'\n') 

file_write.write('\n') 
file_write.close() 
mutation_average.close() 
+4

-1 (a) El OP es ** NO ** "que intenta leer varios archivos grandes en la memoria todos a la vez"; los está leyendo uno a la vez. (b) Sin embargo, el OP dobla la memoria que toma cada archivo cuando lo lee [ver mi respuesta]. (c) Su código simplemente no funcionará; 'totales' y' campo' son objetos ** str **; queremos totales ** numéricos ** para calcular promedios; sus totales van a crecer en cadenas muy largas; esto es Python, no awk; necesita lanzar algunos 'float()' s allí (d) 'totales = [campo para campo en campos]' en lugar de 'totales = campos' ??? –

+0

@John Machin: Buenas capturas: especialmente en la necesidad de convertir los valores de cadena en valores numéricos. Ese 'totals = [field for ...' fue solo un artefacto de un punto en mi codificación donde pensé que necesitaba una copia separada de la lista de campos. – martineau

+0

Seguramente simplificó mi código. – Harpal

7

No, no hay Python -límite específico en el uso de memoria de una aplicación Python. Trabajo regularmente con aplicaciones de Python que pueden usar varios gigabytes de memoria. Lo más probable es que su script realmente use más memoria que la disponible en la máquina en la que se está ejecutando.

En ese caso, la solución es reescribir la secuencia de comandos para que sea más eficiente en cuanto a la memoria o para agregar más memoria física si la secuencia de comandos ya está optimizada para minimizar el uso de la memoria.

Editar:

Su script lee todo el contenido de sus archivos en la memoria a la vez (line = u.readlines()). Dado que está procesando archivos de hasta 20 GB de tamaño, obtendrá errores de memoria con ese enfoque a menos que tenga una gran cantidad de memoria en su máquina.

Un mejor enfoque sería leer los archivos de una línea a la vez:

for u in files: 
    for line in u: # This will iterate over each line in the file 
     # Read values from the line, do necessary calculations 
13

Usted está leyendo el archivo en la memoria (line = u.readlines()), que se producirá un error, por supuesto, si el archivo es demasiado grande (y dices que algunos tienen hasta 20 GB), ese es tu problema allí mismo.

Mejor iterar sobre cada línea:

for current_line in u: 
    do_something_with(current_line) 

es el método recomendado.

Más adelante en su secuencia de comandos, está haciendo cosas muy extrañas, como contar primero todos los elementos de una lista y luego construir un bucle for sobre el rango de ese conteo. ¿Por qué no iterar sobre la lista directamente? ¿Cuál es el propósito de tu script? Tengo la impresión de que esto podría hacerse mucho más fácil.

Esta es una de las ventajas de los lenguajes de alto nivel como Python (en lugar de C donde tiene que hacer estas tareas de limpieza): Permita que Python maneje la iteración por usted, y solo recopile en memoria lo que realmente Necesito tener en la memoria en cualquier momento dado.

Además, como parece que está procesando archivos TSV (valores separados por tabulador), debería echar un vistazo al csv module que se encargará de todas las divisiones, eliminando \n etc. para usted.

13

Python puede usar toda la memoria disponible para su entorno. Mi simple "prueba de memoria" se bloquea en ActiveState Python 2.6 después de usar sobre

1959167 [MiB] 

En jython 2.5 se bloquea antes:

239000 [MiB] 

Es probable que pueda configurar Jython utilizar más memoria (que utiliza límites de JVM)

aplicación

prueba:

import sys 

sl = [] 
i = 0 
# some magic 1024 - overhead of string object 
fill_size = 1024 
if sys.version.startswith('2.7'): 
    fill_size = 1003 
if sys.version.startswith('3'): 
    fill_size = 497 
print(fill_size) 
MiB = 0 
while True: 
    s = str(i).zfill(fill_size) 
    sl.append(s) 
    if i == 0: 
     try: 
      sys.stderr.write('size of one string %d\n' % (sys.getsizeof(s))) 
     except AttributeError: 
      pass 
    i += 1 
    if i % 1024 == 0: 
     MiB += 1 
     if MiB % 25 == 0: 
      sys.stderr.write('%d [MiB]\n' % (MiB)) 

In su aplicación lee todo el archivo de una vez. Para archivos tan grandes, debe leer la línea por línea.

+2

Ejecuto su script en mi máquina (win7-64, python27, 16GB de memoria), se cuelga después de usar 1900 [MiB], pero desde el administrador de tareas sé que la memoria física disponible es de aproximadamente 8000M. Entonces, "Python puede usar toda la memoria disponible para su entorno" puede no ser cierto. – lengxuehx

+1

Estaba equivocado. La razón por la que falla es que un proceso predeterminado de 32 bits obtiene límites de 2 GB en Windows. – lengxuehx

+0

buena prueba; realmente util; thx – Nightingale7

4

No solo está leyendo el total de cada archivo en la memoria, sino que también replica laboriosamente la información en una tabla llamada list_of_lines.

Tiene un problema secundario: sus elecciones de nombres de variables ofuscan severamente lo que está haciendo.

Aquí está su guión reescrito con los readlines() alcaparra retirado y con nombres significativos:

file_A1_B1 = open("A1_B1_100000.txt", "r") 
file_A2_B2 = open("A2_B2_100000.txt", "r") 
file_A1_B2 = open("A1_B2_100000.txt", "r") 
file_A2_B1 = open("A2_B1_100000.txt", "r") 
file_write = open ("average_generations.txt", "w") 
mutation_average = open("mutation_average", "w") # not used 
files = [file_A2_B2,file_A2_B2,file_A1_B2,file_A2_B1] 
for afile in files: 
    table = [] 
    for aline in afile: 
     values = aline.split('\t') 
     values.remove('\n') # why? 
     table.append(values) 
    row_count = len(table) 
    row0length = len(table[0]) 
    print_counter = 4 
    for column_index in range(row0length): 
     column_total = 0 
     for row_index in range(row_count): 
      number = float(table[row_index][column_index]) 
      column_total = column_total + number 
     column_average = column_total/row_count 
     print column_average 
     if print_counter == 4: 
      file_write.write(str(column_average)+'\n') 
      print_counter = 0 
     print_counter +=1 
file_write.write('\n') 

rápidamente se hace evidente que las medias de las columnas (1) se está calculando (2) la ofuscación llevaron a algunos otros a cree que estaba calculando los promedios de las filas.

Al calcular los promedios de las columnas, no se requiere salida hasta el final de cada archivo, y la cantidad de memoria extra realmente requerida es proporcional al número de columnas.

Aquí es una versión revisada del código de bucle externo:

for afile in files: 
    for row_count, aline in enumerate(afile, start=1): 
     values = aline.split('\t') 
     values.remove('\n') # why? 
     fvalues = map(float, values) 
     if row_count == 1: 
      row0length = len(fvalues) 
      column_index_range = range(row0length) 
      column_totals = fvalues 
     else: 
      assert len(fvalues) == row0length 
      for column_index in column_index_range: 
       column_totals[column_index] += fvalues[column_index] 
    print_counter = 4 
    for column_index in column_index_range: 
     column_average = column_totals[column_index]/row_count 
     print column_average 
     if print_counter == 4: 
      file_write.write(str(column_average)+'\n') 
      print_counter = 0 
     print_counter +=1 
+0

No es un gran problema, pero realmente no hay motivo para 'flotar' los valores leídos en una lista separada ni hacer que sus totales (en columnas) sean números reales, solo hay que asegurarse de que sus valores promedio se calculen en ese formato. – martineau

+0

@martineau: si en tu primer punto quieres decir 'values ​​= map (float, values)': detesto tal tipo de cambio. Segundo punto: ¿cómo pueden los totales columnares no ser flotantes ??? –

+0

@John Machin: Quise decir que los valores podrían ser enteros en lugar de flotantes. En el momento en que pensaba que ya lo estaban haciendo, ahora entiendo que, dado que inicialmente son cadenas, deben convertirse a algún tipo de tipo numérico. Dado que estaba pensando que eran enteros, se deduce que sus totales también podrían haber sido, de ahí el segundo punto. Su conversión a 'float' probablemente sea correcta, lo que requeriría que el total sea así también. – martineau

Cuestiones relacionadas