¿Qué significa 'muerto' cuando se procesa un CSV enorme con Python, que se detiene repentinamente?

89

Tengo una secuencia de comandos de Python que importa un archivo CSV grande y luego cuenta el número de ocurrencias de cada palabra en el archivo, luego exporta los recuentos a otro archivo CSV.

Pero lo que está pasando es que una vez que se termina esa parte de conteo y comienza la exportación dice Killeden el terminal.

No creo que esto sea un problema de memoria (si lo fuera, supongo que obtendría un error de memoria y no Killed).

¿Será que el proceso está tardando demasiado? Si es así, ¿hay alguna manera de extender el período de tiempo de espera para evitar esto?

Aquí está el código:

csv.field_size_limit(sys.maxsize)
    counter={}
    with open("/home/alex/Documents/version2/cooccur_list.csv",'rb') as file_name:
        reader=csv.reader(file_name)
        for row in reader:
            if len(row)>1:
                pair=row[0]+' '+row[1]
                if pair in counter:
                    counter[pair]+=1
                else:
                    counter[pair]=1
    print 'finished counting'
    writer = csv.writer(open('/home/alex/Documents/version2/dict.csv', 'wb'))
    for key, value in counter.items():
        writer.writerow([key, value])

Y Killedsucede después de que se finished countinghaya impreso, y el mensaje completo es:

killed (program exited with code: 137)
usuario1893354
fuente
6
Publique la redacción exacta del mensaje de error que está recibiendo.
Robert Harvey
2
"muerto" generalmente significa que el proceso recibió alguna señal que provocó su salida. En este caso, dado que está sucediendo al mismo tiempo que el script, es muy probable que sea una tubería rota, el proceso está tratando de leer o escribir en un identificador de archivo que se ha cerrado en el otro extremo.
Andrew Clark
3
No es una respuesta sobre de dónde killedproviene el mensaje, pero si se debe a que se sobrepasa algún tipo de límite de memoria del sistema, es posible que pueda solucionarlo usando en counter.iteritems()lugar de counter.items()en su ciclo final. En Python 2, itemsdevuelve una lista de claves y valores en el diccionario, que puede requerir mucha memoria si es muy grande. Por el contrario, iteritemses un generador que solo requiere una pequeña cantidad de memoria en un momento dado.
Blckknght

Respuestas:

101

El código de salida 137 (128 + 9) indica que su programa salió debido a la recepción de la señal 9, que es SIGKILL. Esto también explica el killedmensaje. La pregunta es, ¿por qué recibió esa señal?

La razón más probable es probablemente que su proceso cruzó algún límite en la cantidad de recursos del sistema que puede usar. Dependiendo de su sistema operativo y configuración, esto podría significar que tenía demasiados archivos abiertos, utilizó demasiado espacio del sistema de archivos o algo más. Lo más probable es que su programa esté usando demasiada memoria. En lugar de arriesgarse a que las cosas se rompan cuando las asignaciones de memoria comenzaron a fallar, el sistema envió una señal de interrupción al proceso que estaba usando demasiada memoria.

Como comenté anteriormente, una razón por la que puede alcanzar un límite de memoria después de imprimir finished countinges que su llamada a counter.items()en su ciclo final asigna una lista que contiene todas las claves y valores de su diccionario. Si su diccionario tiene muchos datos, esta podría ser una lista muy grande. Una posible solución sería utilizar counter.iteritems()que sea un generador. En lugar de devolver todos los elementos de una lista, le permite iterar sobre ellos con mucho menos uso de memoria.

Entonces, sugiero probar esto, como su ciclo final:

for key, value in counter.iteritems():
    writer.writerow([key, value])

Tenga en cuenta que en Python 3, itemsdevuelve un objeto de "vista de diccionario" que no tiene la misma sobrecarga que la versión de Python 2. Reemplaza iteritems, por lo que si luego actualiza las versiones de Python, terminará cambiando el bucle a la forma en que estaba.

Blckknght
fuente
2
Correcto, pero el diccionario en sí también ocupará mucha memoria. OP debería considerar leer y procesar el archivo de forma incremental en lugar de todo a la vez.
Kevin
24

Hay dos áreas de almacenamiento involucradas: la pila y el montón. La pila es donde se mantiene el estado actual de una llamada a un método (es decir, variables locales y referencias), y el montón es donde se almacenan los objetos. recursividad y memoria

Supongo que hay demasiadas claves en el counterdiccionario que consumirán demasiada memoria de la región del montón, por lo que el tiempo de ejecución de Python generará una excepción OutOfMemory .

Para guardarlo, no cree un objeto gigante, por ejemplo, el contador .

1.StackOverflow

un programa que crea demasiadas variables locales.

Python 2.7.9 (default, Mar  1 2015, 12:57:24) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open('stack_overflow.py','w')
>>> f.write('def foo():\n')
>>> for x in xrange(10000000):
...   f.write('\tx%d = %d\n' % (x, x))
... 
>>> f.write('foo()')
>>> f.close()
>>> execfile('stack_overflow.py')
Killed

2.OutOfMemory

un programa que crea un gigante dictincluye demasiadas claves.

>>> f = open('out_of_memory.py','w')
>>> f.write('def foo():\n')
>>> f.write('\tcounter = {}\n')
>>> for x in xrange(10000000):
...   f.write('counter[%d] = %d\n' % (x, x))
... 
>>> f.write('foo()\n')
>>> f.close()
>>> execfile('out_of_memory.py')
Killed

Referencias
ROY
fuente
2

Dudo que algo esté matando el proceso solo porque lleva mucho tiempo. Killed significa genéricamente que algo externo terminó el proceso, pero probablemente no en este caso presionando Ctrl-C ya que eso haría que Python saliera en una excepción KeyboardInterrupt. Además, en Python obtendría la excepción MemoryError si ese fuera el problema. Lo que podría estar sucediendo es que está encontrando un error en Python o en el código de biblioteca estándar que causa un bloqueo del proceso.

Wingware
fuente
Es mucho más probable que un error que se bloquee dé como resultado un error de segmento que un error SIGKILL, a menos que Python tenga un raise(SIGKILL)código en algún lugar por alguna razón.
Kevin
1
Un error en Python no enviaría SIGKILL.
qwr
2

Lo más probable es que se haya quedado sin memoria, por lo que el Kernel mató su proceso.

¿Has oído hablar de OOM Killer ?

Aquí hay un registro de un script que desarrollé para procesar un gran conjunto de datos de archivos CSV:

Mar 12 18:20:38 server.com kernel: [63802.396693] Out of memory: Kill process 12216 (python3) score 915 or sacrifice child
Mar 12 18:20:38 server.com kernel: [63802.402542] Killed process 12216 (python3) total-vm:9695784kB, anon-rss:7623168kB, file-rss:4kB, shmem-rss:0kB
Mar 12 18:20:38 server.com kernel: [63803.002121] oom_reaper: reaped process 12216 (python3), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

Fue tomado de /var/log/syslog.

Básicamente:

PID 12216 elegido como víctima (debido a su uso de + 9Gb de total-vm), por lo que oom_killer lo cosechó.

Aquí hay un artículo sobre el comportamiento de OOM .

ivanleoncz
fuente
1
+1, solo para aclarar, para comprender cuánta RAM está tratando de usar mi programa, ¿debo sumar los valores total-vm, anon-rss, file-rss? Además, y total-vm da cuánto está usando mi programa y no la memoria real disponible, ¿verdad? Lo siento, conocimiento limitado.
momo
1
Mi conocimiento también es limitado en este contexto, @momo. Estoy un poco fuera de tiempo para más investigaciones, pero encontré esta publicación que podría ayudar: stackoverflow.com/questions/18845857/… . Lo que puedo decirles es que, de hecho, total-vm, es la cantidad de memoria utilizada por el proceso.
ivanleoncz
0

Me pasó lo mismo cuando intenté ejecutar un script de Python desde una carpeta compartida VirtualBoxdentro del nuevo Ubuntu 20.04 LTS. Python rescató Killedmientras cargaba mi propia biblioteca personal. Cuando moví la carpeta a un directorio local, el problema desapareció. Parece que la Killedparada ocurrió durante las importaciones iniciales de mi biblioteca, ya que recibí mensajes de bibliotecas faltantes una vez que moví la carpeta.

El problema desapareció después de que reinicié mi computadora.

Por lo tanto, es posible que las personas quieran intentar mover el programa a un directorio local si se trata de un recurso compartido de algún tipo o podría ser un problema transitorio que solo requiere reiniciar el sistema operativo.

Timothy C. Quinn
fuente
Espere, ¿tuvo que reiniciar su host o la máquina virtual?
cglacet
Si. En mi caso, estaba construyendo una nueva máquina virtual y acababa de instalar Python cuando vi este problema. Después de reiniciar, desapareció. Odio reiniciar como una forma de arreglar las cosas, así que pasé un montón de tiempo tratando de depurar y después de una hora de excavación, incluso aquí en SO. Pero finalmente, me di por vencido y reinicié y listo. No tengo idea de por qué funcionó.
Timothy C. Quinn