Что означает «убит», когда обработка огромного CSV с помощью Python внезапно останавливается?

89

У меня есть сценарий Python, который импортирует большой файл CSV, а затем подсчитывает количество вхождений каждого слова в файле, а затем экспортирует счетчики в другой файл CSV.

Но происходит следующее: как только счетная часть завершается и начинается экспорт, это отображается Killedв терминале.

Я не думаю, что это проблема с памятью (если бы это было так, я предполагаю, что получил бы ошибку памяти, а не Killed).

Может быть, процесс затягивается? Если да, то есть ли способ продлить период ожидания, чтобы этого избежать?

Вот код:

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

И это Killedпроисходит после того, finished countingкак напечатано, и полное сообщение:

killed (program exited with code: 137)
user1893354
источник
6
Опубликуйте точную формулировку получаемого сообщения об ошибке.
Роберт Харви
2
«убит» обычно означает, что процесс получил сигнал, который заставил его выйти. В этом случае, поскольку это происходит одновременно со сценарием, есть большая вероятность, что это сломанный канал, процесс пытается читать или записывать в дескриптор файла, который был закрыт на другом конце.
Эндрю Кларк
3
Это не ответ о том, откуда killedпришло сообщение, но если это связано с превышением какого-то ограничения системной памяти, вы можете исправить это, используя counter.iteritems()вместо этого counter.items()в своем последнем цикле. В Python 2 itemsвозвращает список ключей и значений в словаре, для которого может потребоваться много памяти, если он очень большой. Напротив, iteritemsэто генератор, которому в любой момент времени требуется только небольшой объем памяти.
Blckknght

Ответы:

101

Код выхода 137 (128 + 9) указывает, что ваша программа завершилась из-за приема сигнала 9, который есть SIGKILL. Это также объясняет killedсообщение. Вопрос в том, почему вы получили этот сигнал?

Скорее всего, причина в том, что ваш процесс преодолел некоторый предел количества системных ресурсов, которые вам разрешено использовать. В зависимости от вашей ОС и конфигурации это может означать, что у вас было слишком много открытых файлов, использовалось слишком много места в файловой системе или что-то еще. Скорее всего, ваша программа использовала слишком много памяти. Вместо того, чтобы рисковать, что что-то сломается, когда выделение памяти начало давать сбой, система отправила сигнал уничтожения процессу, который использовал слишком много памяти.

Как я прокомментировал ранее, одна из причин, по которой вы можете достичь ограничения памяти после печати, finished countingзаключается в том, что ваш вызов counter.items()в вашем последнем цикле выделяет список, содержащий все ключи и значения из вашего словаря. Если в вашем словаре было много данных, это может быть очень большой список. Возможное решение counter.iteritems()- использовать генератор. Вместо того, чтобы возвращать все элементы в списке, он позволяет вам перебирать их с гораздо меньшим использованием памяти.

Итак, я предлагаю попробовать это в качестве последнего цикла:

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

Обратите внимание, что в Python 3 itemsвозвращает объект «просмотр словаря», который не имеет таких же накладных расходов, как версия Python 2. Он заменяет iteritems, поэтому, если вы позже обновите версии Python, вы в конечном итоге вернете цикл к тому, как он был.

Blckknght
источник
2
Верно, но сам словарь тоже займет много памяти. OP следует рассмотреть возможность чтения и обработки файла постепенно, а не сразу.
Кевин
24

Здесь задействованы две области хранения: стек и куча. В стеке хранится текущее состояние вызова метода (т. Е. Локальные переменные и ссылки), а в куче хранятся объекты. рекурсия и память

Я предполагаю, что в counterdict слишком много ключей, которые будут использовать слишком много памяти в области кучи, поэтому среда выполнения Python вызовет исключение OutOfMemory .

Чтобы спасти его, не создавайте гигантский объект, например, счетчик .

1.StackOverflow

программа, которая создает слишком много локальных переменных.

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

программа, создающая гиганта, dictвключает слишком много ключей.

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

Ссылки
РОЙ
источник
2

Я сомневаюсь, что что-то убивает процесс только потому, что он занимает много времени. Killed обычно означает, что что-то извне завершило процесс, но, вероятно, не в этом случае нажатие Ctrl-C, так как это приведет к завершению Python при исключении KeyboardInterrupt. Кроме того, в Python вы получите исключение MemoryError, если это проблема. Возможно, вы столкнулись с ошибкой в ​​коде Python или стандартной библиотеки, которая вызывает сбой процесса.

Wingware
источник
Ошибка, вызывающая сбой, с гораздо большей вероятностью приведет к segfault, чем к получению SIGKILL, если Python raise(SIGKILL)по какой-либо причине не имеет где-то в своем коде.
Кевин
1
Ошибка в python не отправляет SIGKILL.
qwr
2

Скорее всего, у вас закончилась память, поэтому ядро ​​убило ваш процесс.

Вы слышали об OOM Killer ?

Вот журнал сценария, который я разработал для обработки огромного набора данных из файлов 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

Это было взято из /var/log/syslog.

В принципе:

PID 12216 выбран жертвой (из-за использования + 9 ГБ total-vm), поэтому oom_killer пожинал его.

Вот статья о поведении OOM .

ivanleoncz
источник
1
+1, просто чтобы уточнить, чтобы понять, сколько оперативной памяти пытается использовать моя программа, следует ли мне сложить значения total-vm, anon-rss, file-rss? Также и total-vm показывает, сколько использует моя программа, а не фактическая доступная память, верно? Извините, знания ограничены.
momo
1
Мои знания также ограничены в этом контексте, @momo. У меня немного нет времени для дальнейших исследований, но я нашел этот пост, который может помочь: stackoverflow.com/questions/18845857/… . Что я могу вам сказать, так это то, что total-vm - это объем памяти, используемый процессом.
ivanleoncz
0

То же самое случилось со мной, когда я попытался запустить скрипт python из общей папки в VirtualBoxновом Ubuntu 20.04 LTS. Python отказался от Killedзагрузки моей личной библиотеки. Когда я переместил папку в локальный каталог, проблема исчезла. Похоже, что Killedостановка произошла во время первоначального импорта моей библиотеки, поскольку я получил сообщения об отсутствующих библиотеках после того, как переместил папку.

Проблема исчезла после того, как я перезапустил свой компьютер.

Следовательно, люди могут захотеть попробовать переместить программу в локальный каталог, если он превышает какой-либо общий ресурс или это может быть временная проблема, требующая просто перезагрузки ОС.

Тимоти К. Куинн
источник
Подождите, вам пришлось перезагрузить хост или виртуальную машину?
cglacet
Да. В моем случае я создавал новую виртуальную машину и только что установил Python, когда увидел эту проблему. После перезагрузки ушло. Я ненавижу перезагрузку как способ исправить ситуацию, поэтому я потратил кучу времени, пытаясь отладить, и после часа копания, в том числе здесь, в SO. Но в конце концов я сдался, перезагрузился и престо. Понятия не имею, почему это сработало.
Тимоти К. Куинн