O que significa 'morto' quando o processamento de um CSV enorme com Python, que para repentinamente?

89

Eu tenho um script Python que importa um grande arquivo CSV e conta o número de ocorrências de cada palavra no arquivo, depois exporta as contagens para outro arquivo CSV.

Mas o que está acontecendo é que uma vez finalizada a contagem e iniciada a exportação diz-se Killedno terminal.

Não acho que seja um problema de memória (se fosse, suponho que receberia um erro de memória e não Killed).

Será que o processo está demorando muito? Em caso afirmativo, há uma maneira de estender o período de tempo limite para que eu possa evitar isso?

Aqui está o 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])

E o Killedacontece depois de finished countingimpresso, e a mensagem completa é:

killed (program exited with code: 137)
user1893354
fonte
6
Publique o texto exato da mensagem de erro que você está recebendo.
Robert Harvey,
2
"eliminado" geralmente significa que o processo recebeu algum sinal que o fez sair. Nesse caso, como está acontecendo ao mesmo tempo que o script, há uma boa chance de que seja um pipe quebrado, o processo está tentando ler ou gravar em um identificador de arquivo que foi fechado na outra extremidade.
Andrew Clark,
3
Não é uma resposta sobre de onde killedvem a mensagem, mas se for devido a ultrapassar algum tipo de limite de memória do sistema, você pode consertar isso usando em counter.iteritems()vez de counter.items()em seu loop final. No Python 2, itemsretorna uma lista das chaves e valores no dicionário, o que pode exigir muita memória se for muito grande. Em contraste, iteritemsé um gerador que requer apenas uma pequena quantidade de memória em um determinado momento.
Blckknght

Respostas:

101

O código de saída 137 (128 + 9) indica que seu programa saiu devido ao recebimento do sinal 9, que é SIGKILL. Isso também explica a killedmensagem. A questão é: por que você recebeu esse sinal?

O motivo mais provável é que seu processo ultrapassou algum limite na quantidade de recursos do sistema que você tem permissão para usar. Dependendo do seu sistema operacional e da configuração, isso pode significar que você tem muitos arquivos abertos, usa muito espaço no sistema de arquivos ou algo mais. O mais provável é que seu programa estava usando muita memória. Em vez de arriscar que as coisas quebrem quando as alocações de memória começarem a falhar, o sistema enviou um sinal de eliminação para o processo que estava usando muita memória.

Como comentei anteriormente, um motivo pelo qual você pode atingir um limite de memória após a impressão finished countingé que sua chamada a counter.items()em seu loop final aloca uma lista que contém todas as chaves e valores de seu dicionário. Se o seu dicionário tiver muitos dados, esta pode ser uma lista muito grande. Uma possível solução seria usar counter.iteritems()qual é um gerador. Em vez de retornar todos os itens em uma lista, permite iterar sobre eles com muito menos uso de memória.

Portanto, sugiro tentar isso, como seu loop final:

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

Observe que no Python 3, itemsretorna um objeto de "visualização de dicionário" que não tem a mesma sobrecarga que a versão do Python 2. Ele substitui iteritems, portanto, se você atualizar posteriormente as versões do Python, acabará alterando o loop de volta ao que era.

Blckknght
fonte
2
Correto, mas o próprio dicionário também vai ocupar muita memória. O OP deve considerar a leitura e o processamento do arquivo de forma incremental em vez de tudo de uma vez.
Kevin
24

Existem duas áreas de armazenamento envolvidas: a pilha e o heap. A pilha é onde o estado atual de uma chamada de método é mantido (isto é, variáveis ​​locais e referências), e o heap é onde os objetos são armazenados. recursão e memória

Imagino que haja muitas chaves no counterdicionário que consumirão muita memória da região de heap, portanto, o tempo de execução do Python gerará uma exceção OutOfMemory .

Para salvá-lo, não crie um objeto gigante, por exemplo, o balcão .

1.StackOverflow

um programa que cria muitas variáveis ​​locais.

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

um programa que cria um gigante dictinclui muitas chaves.

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

Referências
ROY
fonte
2

Duvido que qualquer coisa esteja matando o processo só porque leva muito tempo. Killed genericamente significa que algo externo encerrou o processo, mas provavelmente não neste caso pressionando Ctrl-C, pois isso faria com que o Python fechasse em uma exceção KeyboardInterrupt. Além disso, em Python você obteria a exceção MemoryError se esse fosse o problema. O que pode estar acontecendo é que você está encontrando um bug no Python ou no código da biblioteca padrão que causa uma falha no processo.

Wingware
fonte
Um bug de falha teria muito mais probabilidade de resultar em um segfault do que em obtê-lo SIGKILL, a menos que o Python tenha um raise(SIGKILL)algum lugar em seu código por algum motivo.
Kevin
1
Um bug em python não enviaria SIGKILL.
qwr
2

Provavelmente, você ficou sem memória, então o Kernel matou seu processo.

Você já ouviu falar sobre OOM Killer ?

Aqui está um registro de um script que desenvolvi para processar um grande conjunto de dados de arquivos 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

Foi tirado de /var/log/syslog .

Basicamente:

PID 12216 eleito como vítima (devido ao uso de + 9Gb de total-vm), então oom_killer colheu isso.

Aqui está um artigo sobre o comportamento OOM .

Ivanleoncz
fonte
1
+1, só para esclarecer, para entender quanta RAM meu programa está tentando usar, devo somar os valores total-vm, anon-rss, arquivo-rss? Além disso, e total-vm fornece quanto meu programa está usando e não a memória real disponível, certo? Desculpe, conhecimento limitado.
momo
1
Meu conhecimento também é limitado neste contexto, @momo. Estou um pouco sem tempo para mais investigações, mas encontrei este post que pode ajudar: stackoverflow.com/questions/18845857/… . O que posso dizer é que, de fato, total-vm, é a quantidade de memória usada pelo processo.
ivanleoncz
0

O mesmo aconteceu comigo quando tentei executar um script Python de uma pasta compartilhada no VirtualBoxnovo Ubuntu 20.04 LTS. Python se recuperou Killedenquanto carregava minha biblioteca pessoal. Quando movi a pasta para um diretório local, o problema foi embora. Parece que oKilled parada aconteceu durante as importações iniciais da minha biblioteca, pois recebi mensagens de bibliotecas ausentes ao mover a pasta.

O problema foi embora depois que reiniciei meu computador.

Portanto, as pessoas podem querer tentar mover o programa para um diretório local se for sobre um compartilhamento de algum tipo ou pode ser um problema temporário que requer apenas a reinicialização do sistema operacional.

Timothy C. Quinn
fonte
Espere, você teve que reiniciar seu host ou a VM?
cglacet
Sim. No meu caso, eu estava construindo uma nova VM e tinha acabado de instalar o Python quando vi esse problema. Após a reinicialização, ele foi embora. Eu odeio reiniciar como forma de consertar as coisas, então passei muito tempo tentando depurar e depois de uma hora de escavação, inclusive aqui no SO. Mas, eventualmente, desisti e reiniciei e pronto. Não tenho ideia de por que funcionou.
Timothy C. Quinn,