Cosa significa "ucciso" quando l'elaborazione di un enorme CSV con Python, che si interrompe improvvisamente?


94

Ho uno script Python che importa un file CSV di grandi dimensioni e quindi conta il numero di occorrenze di ogni parola nel file, quindi esporta i conteggi in un altro file CSV.

Ma quello che sta succedendo è che una volta che la parte di conteggio è finita e l'esportazione inizia, viene detto Killednel terminale.

Non penso che questo sia un problema di memoria (se lo fosse, presumo che avrei ricevuto un errore di memoria e non Killed).

Potrebbe essere che il processo stia impiegando troppo tempo? In tal caso, esiste un modo per estendere il periodo di timeout in modo da poterlo evitare?

Ecco il codice:

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])

E Killedsuccede dopo che finished countingè stato stampato, e il messaggio completo è:

killed (program exited with code: 137)

6
Pubblica la formulazione esatta del messaggio di errore che stai ricevendo.
Robert Harvey

2
"ucciso" generalmente significa che il processo ha ricevuto un segnale che ne ha provocato l'uscita. In questo caso, poiché sta accadendo nello stesso momento dello script, ci sono buone probabilità che si tratti di una pipe rotta, il processo sta cercando di leggere o scrivere su un handle di file che è stato chiuso dall'altra parte.
Andrew Clark

3
Non è una risposta sulla killedprovenienza del messaggio, ma se è dovuto al superamento di una sorta di limite di memoria di sistema, potresti essere in grado di risolverlo utilizzando counter.iteritems()invece che counter.items()nel tuo ciclo finale. In Python 2, itemsrestituisce un elenco delle chiavi e dei valori nel dizionario, che potrebbe richiedere molta memoria se è molto grande. Al contrario, iteritemsè un generatore che richiede solo una piccola quantità di memoria in un dato momento.
Blckknght

Risposte:


104

Il codice di uscita 137 (128 + 9) indica che il programma è terminato a causa della ricezione del segnale 9, ovvero SIGKILL. Questo spiega anche il killedmessaggio. La domanda è: perché hai ricevuto quel segnale?

Il motivo più probabile è probabilmente che il tuo processo ha superato un limite nella quantità di risorse di sistema che puoi utilizzare. A seconda del sistema operativo e della configurazione, ciò potrebbe significare che c'erano troppi file aperti, utilizzato troppo spazio per il file system o qualcos'altro. La più probabile è che il tuo programma stia utilizzando troppa memoria. Invece di rischiare che le cose si interrompano quando le allocazioni di memoria hanno iniziato a fallire, il sistema ha inviato un segnale di kill al processo che utilizzava troppa memoria.

Come ho commentato in precedenza, uno dei motivi per cui potresti raggiungere un limite di memoria dopo la stampa finished countingè che la tua chiamata a counter.items()nel tuo ciclo finale alloca un elenco che contiene tutte le chiavi e i valori dal tuo dizionario. Se il tuo dizionario conteneva molti dati, questo potrebbe essere un elenco molto grande. Una possibile soluzione sarebbe quella di utilizzare counter.iteritems()che è un generatore. Piuttosto che restituire tutti gli elementi in un elenco, ti consente di iterarli con un utilizzo della memoria molto inferiore.

Quindi, suggerirei di provare questo, come ciclo finale:

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

Nota che in Python 3, itemsrestituisce un oggetto "vista dizionario" che non ha lo stesso overhead della versione di Python 2. Sostituisce iteritems, quindi se in seguito aggiorni le versioni di Python, finirai per riportare il ciclo al modo in cui era.


2
È corretto, ma anche il dizionario stesso occuperà molta memoria. L'OP dovrebbe prendere in considerazione la lettura e l'elaborazione del file in modo incrementale anziché tutto in una volta.
Kevin

24

Ci sono due aree di archiviazione coinvolte: lo stack e l'heap. Lo stack è dove viene mantenuto lo stato corrente di una chiamata al metodo (cioè variabili locali e riferimenti) e l'heap è dove vengono memorizzati gli oggetti. ricorsione e memoria

Immagino che ci siano troppe chiavi nel counterdict che consumeranno troppa memoria della regione dell'heap, quindi il runtime Python solleverà un'eccezione OutOfMemory .

Per salvarlo, non creare un oggetto gigante, ad esempio il contatore .

1. StackOverflow

un programma che crea troppe variabili locali.

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 programma che crea un gigante dictinclude troppe chiavi.

>>> 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

Riferimenti

4

Molto probabilmente, hai esaurito la memoria, quindi il kernel ha interrotto il tuo processo.

Hai sentito parlare di OOM Killer ?

Ecco un registro da uno script che ho sviluppato per l'elaborazione di un enorme set di dati da file 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

È stato preso da /var/log/syslog.

Fondamentalmente:

PID 12216 è stato eletto vittima (a causa del suo utilizzo di + 9Gb di total-vm), quindi oom_killer l'ha raccolto.

Ecco un articolo sul comportamento dell'OOM .


1
+1, solo per chiarire, per capire quanta RAM il mio programma sta cercando di utilizzare, devo sommare i valori total-vm, anon-rss, file-rss? Inoltre e total-vm fornisce quanto sta utilizzando il mio programma e non l'effettiva memoria disponibile, giusto? Scusa, conoscenza limitata.
momo

1
Anche la mia conoscenza è limitata in questo contesto, @momo. Sono un po 'fuori dal tempo per ulteriori indagini, ma ho trovato questo post che potrebbe aiutare: stackoverflow.com/questions/18845857/… . Quello che posso dirti è che in effetti total-vm è la quantità di memoria utilizzata dal processo.
ivanleoncz

3

Dubito che qualcosa stia uccidendo il processo solo perché richiede molto tempo. Ucciso genericamente significa che qualcosa dall'esterno ha terminato il processo, ma probabilmente non in questo caso premendo Ctrl-C poiché ciò causerebbe l'uscita di Python su un'eccezione KeyboardInterrupt. Inoltre, in Python si otterrebbe l'eccezione MemoryError se questo fosse il problema. Quello che potrebbe succedere è che stai colpendo un bug in Python o nel codice della libreria standard che causa un arresto anomalo del processo.


Un bug che si blocca sarebbe molto più probabile che si traduca in un segfault che nell'ottenere SIGKILL, a meno che Python non abbia da raise(SIGKILL)qualche parte nel suo codice per qualche motivo.
Kevin

1
Un bug in python non inviava SIGKILL.
qwr

1

Mi è accaduto lo stesso quando ho provato a eseguire uno script python da una cartella condivisa VirtualBoxall'interno del nuovo Ubuntu 20.04 LTS. Python è stato salvato Killeddurante il caricamento della mia libreria personale. Quando ho spostato la cartella in una directory locale, il problema è stato risolto. Sembra che l' Killedinterruzione sia avvenuta durante le importazioni iniziali della mia libreria poiché ho ricevuto messaggi di librerie mancanti una volta spostata la cartella.

Il problema è scomparso dopo aver riavviato il computer.

Pertanto, le persone potrebbero voler provare a spostare il programma in una directory locale se si trova su una condivisione di qualche tipo o potrebbe essere un problema temporaneo che richiede solo un riavvio del sistema operativo.


Aspetta, hai dovuto riavviare il tuo host o la VM?
cglacet

1
Sì. Nel mio caso, stavo costruendo una nuova VM e avevo appena installato Python quando ho visto questo problema. Dopo il riavvio, è andato via. Odio il riavvio come un modo per sistemare le cose, quindi ho passato un sacco di tempo a provare a eseguire il debug e dopo un'ora di ricerca, anche qui in SO. Ma alla fine, ho rinunciato e riavviato e presto. Non ho idea del perché abbia funzionato.
Timothy C. Quinn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.