Que signifie «tué» lorsqu'un traitement d'un énorme CSV avec Python, qui s'arrête soudainement?

89

J'ai un script Python qui importe un gros fichier CSV, puis compte le nombre d'occurrences de chaque mot dans le fichier, puis exporte les comptes vers un autre fichier CSV.

Mais ce qui se passe, c'est qu'une fois que cette partie de comptage est terminée et que l'exportation commence, il est dit Killeddans le terminal.

Je ne pense pas que ce soit un problème de mémoire (si c'était le cas, je suppose que j'aurais une erreur de mémoire et non Killed).

Se pourrait-il que le processus prenne trop de temps? Si tel est le cas, existe-t-il un moyen de prolonger le délai d'expiration afin que je puisse éviter cela?

Voici le code:

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

Et ce qui Killedse passe après finished countings'est imprimé, et le message complet est:

killed (program exited with code: 137)
user1893354
la source
6
Postez le libellé exact du message d'erreur que vous recevez.
Robert Harvey
2
«tué» signifie généralement que le processus a reçu un signal qui l'a fait quitter. Dans ce cas, comme cela se produit en même temps que le script, il y a de fortes chances que ce soit un tuyau cassé, le processus essaie de lire ou d'écrire dans un descripteur de fichier qui a été fermé à l'autre extrémité.
Andrew Clark
3
Ce n'est pas une réponse sur la killedprovenance du message, mais si cela est dû au dépassement d'une sorte de limite de mémoire système, vous pourrez peut-être résoudre ce problème en utilisant counter.iteritems()au lieu de counter.items()dans votre boucle finale. Dans Python 2, itemsrenvoie une liste des clés et des valeurs du dictionnaire, ce qui peut nécessiter beaucoup de mémoire s'il est très volumineux. En revanche, iteritemsest un générateur qui ne nécessite qu'une petite quantité de mémoire à un moment donné.
Blckknght

Réponses:

101

Le code de sortie 137 (128 + 9) indique que votre programme s'est arrêté en raison de la réception du signal 9, qui est SIGKILL. Cela explique également le killedmessage. La question est, pourquoi avez-vous reçu ce signal?

La raison la plus probable est probablement que votre processus a dépassé une certaine limite dans la quantité de ressources système que vous êtes autorisé à utiliser. En fonction de votre système d'exploitation et de votre configuration, cela peut signifier que vous avez trop de fichiers ouverts, utilisé trop d'espace dans le système de fichiers ou autre chose. Le plus probable est que votre programme utilisait trop de mémoire. Plutôt que de risquer des ruptures lorsque les allocations de mémoire ont commencé à échouer, le système a envoyé un signal d'arrêt au processus qui utilisait trop de mémoire.

Comme je l'ai commenté plus tôt, une des raisons pour lesquelles vous pourriez atteindre une limite de mémoire après l'impression finished countingest que votre appel à counter.items()dans votre boucle finale alloue une liste qui contient toutes les clés et valeurs de votre dictionnaire. Si votre dictionnaire contenait beaucoup de données, cela pourrait être une très grande liste. Une solution possible serait d'utiliser counter.iteritems()qui est un générateur. Plutôt que de renvoyer tous les éléments d'une liste, il vous permet de les parcourir avec beaucoup moins de mémoire.

Donc, je suggérerais d'essayer ceci, comme dernière boucle:

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

Notez qu'en Python 3, itemsretourne un objet "vue dictionnaire" qui n'a pas la même surcharge que la version de Python 2. Il remplace iteritems, donc si vous mettez à niveau plus tard les versions de Python, vous finirez par changer la boucle comme elle était.

Blckknght
la source
2
Correct, mais le dictionnaire lui-même prendra également beaucoup de mémoire. OP devrait envisager de lire et de traiter le fichier de manière incrémentielle plutôt que d'un seul coup.
Kevin
24

Il y a deux zones de stockage impliquées: la pile et le tas. La pile est l'endroit où l'état actuel d'un appel de méthode est conservé (c.-à-d. Les variables locales et les références), et le tas est l'endroit où les objets sont stockés. récursion et mémoire

Je pense qu'il y a trop de clés dans le counterdict qui consommeront trop de mémoire de la région du tas, donc le runtime Python lèvera une exception OutOfMemory .

Pour le sauvegarder, ne créez pas d'objet géant, par exemple le compteur .

1.StackOverflow

un programme qui crée trop de 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 programme qui crée un géant dictcomprend trop de clés.

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

Références
ROY
la source
2

Je doute que quoi que ce soit tue le processus simplement parce que cela prend beaucoup de temps. Tué signifie génériquement que quelque chose de l'extérieur a mis fin au processus, mais probablement pas dans ce cas en appuyant sur Ctrl-C car cela provoquerait la fermeture de Python sur une exception KeyboardInterrupt. De plus, en Python, vous obtiendrez une exception MemoryError si c'était le problème. Ce qui peut se passer, c'est que vous rencontrez un bogue dans Python ou dans le code de bibliothèque standard qui provoque un blocage du processus.

Wingware
la source
Un bogue qui plante serait beaucoup plus susceptible d'entraîner une erreur de segmentation que d'obtenir SIGKILL, à moins que Python n'ait un raise(SIGKILL)quelque part dans son code pour une raison quelconque.
Kevin
1
Un bogue en python n'enverrait pas SIGKILL.
qwr
2

Très probablement, vous avez manqué de mémoire, donc le noyau a tué votre processus.

Avez-vous entendu parler de OOM Killer ?

Voici un journal d'un script que j'ai développé pour traiter un énorme ensemble de données à partir de fichiers 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

Il a été pris de /var/log/syslog.

Fondamentalement:

PID 12216 élu comme victime (en raison de son utilisation de + 9 Go de total-vm), donc oom_killer l'a récolté.

Voici un article sur le comportement du MOO .

Ivanleoncz
la source
1
+1, juste pour clarifier, pour comprendre la quantité de RAM que mon programme essaie d'utiliser, dois-je additionner les valeurs total-vm, anon-rss, file-rss? De plus, total-vm indique combien mon programme utilise et non la mémoire disponible réelle, n'est-ce pas? Désolé, connaissances limitées.
momo
1
Mes connaissances sont également limitées sur ce contexte, @momo. Je suis un peu à court de temps pour d'autres investigations, mais j'ai trouvé ce post qui pourrait aider: stackoverflow.com/questions/18845857/… . Ce que je peux vous dire, c'est qu'en effet, total-vm, c'est la quantité de mémoire utilisée par le processus.
ivanleoncz le
0

J'ai juste eu la même chose sur moi lorsque j'ai essayé d'exécuter un script python à partir d'un dossier partagé VirtualBoxdans le nouveau Ubuntu 20.04 LTS. Python s'est sauvé Killedlors du chargement de ma propre bibliothèque personnelle. Lorsque j'ai déplacé le dossier vers un répertoire local, le problème a disparu. Il semble que leKilled arrêt s'est produit lors des importations initiales de ma bibliothèque car j'ai reçu des messages de bibliothèques manquantes une fois que j'ai déplacé le dossier.

Le problème a disparu après le redémarrage de mon ordinateur.

Par conséquent, les gens peuvent vouloir essayer de déplacer le programme vers un répertoire local s'il s'agit d'un partage quelconque ou s'il peut s'agir d'un problème transitoire qui nécessite simplement un redémarrage du système d'exploitation.

Timothy C. Quinn
la source
Attendez, vous avez dû redémarrer votre hôte ou la VM?
cglacet
Oui. Dans mon cas, je construisais une nouvelle VM et je venais d'installer Python quand j'ai vu ce problème. Après le redémarrage, il est parti. Je déteste le redémarrage comme moyen de réparer les choses, alors j'ai passé beaucoup de temps à essayer de déboguer et après une heure de fouille, y compris ici dans SO. Mais finalement, j'ai abandonné et redémarré et hop. Je n'ai aucune idée de pourquoi cela a fonctionné.
Timothy C. Quinn