Co oznacza „zabity”, gdy przetwarzanie ogromnego pliku CSV za pomocą Pythona nagle się zatrzymuje?

89

Mam skrypt w języku Python, który importuje duży plik CSV, a następnie zlicza liczbę wystąpień każdego słowa w pliku, a następnie eksportuje liczby do innego pliku CSV.

Ale to, co się dzieje, to to, że po zakończeniu liczenia części i rozpoczęciu eksportu, Killedna terminalu jest napisane .

Nie sądzę, że jest to problem z pamięcią (zakładam, że wystąpiłby błąd pamięci, a nie Killed).

Czy to możliwe, że ten proces trwa zbyt długo? Jeśli tak, czy istnieje sposób na przedłużenie limitu czasu, abym mógł tego uniknąć?

Oto kod:

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

A Killeddzieje się po finished countingwydrukowaniu, a pełna wiadomość to:

killed (program exited with code: 137)
user1893354
źródło
6
Opublikuj dokładną treść komunikatu o błędzie, który otrzymujesz.
Robert Harvey
2
„zabity” oznacza ogólnie, że proces otrzymał sygnał, który spowodował jego zakończenie. W tym przypadku, ponieważ dzieje się to w tym samym czasie skryptu, istnieje duże prawdopodobieństwo, że jest to uszkodzony potok, a proces próbuje odczytać lub zapisać do uchwytu pliku, który został zamknięty na drugim końcu.
Andrew Clark
3
Nie jest to odpowiedź na temat tego, skąd killedpochodzi wiadomość, ale jeśli wynika to z przekroczenia pewnego rodzaju limitu pamięci systemowej, możesz to naprawić, używając counter.iteritems()zamiast counter.items()w ostatniej pętli. W Pythonie 2 itemszwraca listę kluczy i wartości w słowniku, który może wymagać dużo pamięci, jeśli jest bardzo duży. W przeciwieństwie do tego iteritemsjest to generator, który wymaga tylko niewielkiej ilości pamięci w danym momencie.
Blckknght

Odpowiedzi:

101

Kod zakończenia 137 (128 + 9) wskazuje, że program zakończył pracę z powodu odebrania sygnału 9, czyli SIGKILL. To również wyjaśnia killedprzesłanie. Pytanie brzmi, dlaczego otrzymałeś ten sygnał?

Najbardziej prawdopodobną przyczyną jest prawdopodobnie przekroczenie przez proces pewnego ograniczenia ilości zasobów systemowych, których możesz używać. W zależności od systemu operacyjnego i konfiguracji może to oznaczać, że masz zbyt wiele otwartych plików, zużyłeś zbyt dużo miejsca w pamięci plików lub coś innego. Najprawdopodobniej Twój program zużywał zbyt dużo pamięci. Zamiast ryzykować, że coś się zepsuje, gdy alokacje pamięci zaczną się nie powieść, system wysłał sygnał zabicia do procesu, który zużywał zbyt dużo pamięci.

Jak wspomniałem wcześniej, jednym z powodów, dla których możesz przekroczyć limit pamięci po wydrukowaniu, finished countingjest to, że twoje wywołanie counter.items()w ostatniej pętli przydziela listę zawierającą wszystkie klucze i wartości z twojego słownika. Jeśli Twój słownik zawiera dużo danych, może to być bardzo duża lista. Możliwym rozwiązaniem byłoby użycie counter.iteritems()generatora. Zamiast zwracać wszystkie elementy z listy, umożliwia ich iterację przy znacznie mniejszym zużyciu pamięci.

Proponuję więc wypróbować to jako ostatnią pętlę:

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

Zauważ, że w Pythonie 3 itemszwraca obiekt „widoku słownika”, który nie ma takiego samego narzutu, jak wersja Pythona 2. Zastępuje iteritems, więc jeśli później zaktualizujesz wersje Pythona, w końcu zmienisz pętlę z powrotem do stanu, w jakim była.

Blckknght
źródło
2
Dobrze, ale sam słownik również zajmie dużo pamięci. OP powinien rozważyć odczyt i przetwarzanie pliku przyrostowo zamiast wszystkich naraz.
Kevin
24

W grę wchodzą dwa obszary przechowywania: stos i sterta. Stos jest miejscem, w którym przechowywany jest bieżący stan wywołania metody (tj. Zmienne lokalne i odwołania), a sterta to miejsce, w którym przechowywane są obiekty. rekurencja i pamięć

Wydaje mi się, że w dyktycie jest zbyt wiele kluczy, counterktóre będą zużywać zbyt dużo pamięci regionu sterty, więc środowisko wykonawcze Pythona zgłosi wyjątek OutOfMemory .

Aby go zapisać, nie twórz gigantycznego obiektu, np. Licznika .

1.StackOverflow

program, który tworzy zbyt wiele zmiennych lokalnych.

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

program, który tworzy giganta, dictzawiera zbyt wiele kluczy.

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

Bibliografia
ROY
źródło
2

Wątpię, żeby cokolwiek zabijało ten proces tylko dlatego, że zajmuje dużo czasu. Zabicie generalnie oznacza, że ​​coś z zewnątrz zakończyło proces, ale prawdopodobnie nie w tym przypadku naciśnięcie Ctrl-C, ponieważ spowodowałoby to zamknięcie Pythona w przypadku wyjątku KeyboardInterrupt. Ponadto w Pythonie można uzyskać wyjątek MemoryError, jeśli to był problem. To, co może się dziać, to błąd w Pythonie lub standardowym kodzie biblioteki, który powoduje awarię procesu.

Wingware
źródło
Awaria powodująca awarię byłaby znacznie bardziej prawdopodobna w wyniku segfault niż uzyskanie SIGKILL, chyba że Python ma raise(SIGKILL)gdzieś w swoim kodzie z jakiegoś powodu.
Kevin
1
Błąd w Pythonie nie wysyłał SIGKILL.
qwr
2

Najprawdopodobniej zabrakło pamięci, więc Kernel zabił twój proces.

Czy słyszałeś o OOM Killer ?

Oto dziennik ze skryptu, który opracowałem do przetwarzania ogromnego zestawu danych z plików 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

Został wzięty z /var/log/syslog.

Gruntownie:

PID 12216 został wybrany jako ofiara (ze względu na użycie + 9 Gb total-vm), więc oom_killer go zebrał.

Oto artykuł o zachowaniu OOM .

ivanleoncz
źródło
1
+1, żeby wyjaśnić, czy aby zrozumieć, ile pamięci RAM próbuje użyć mój program, czy powinienem dodać wartości total-vm, anon-rss, file-rss? Ponadto, a total-vm podaje, ile używa mojego programu, a nie rzeczywistą dostępną pamięć, prawda? Przepraszamy, wiedza ograniczona.
momo
1
Moja wiedza w tym kontekście również jest ograniczona, @momo. Trochę brakuje mi czasu na dalsze badania, ale znalazłem ten post, który może pomóc: stackoverflow.com/questions/18845857/… . Mogę ci powiedzieć, że rzeczywiście, total-vm, to ilość pamięci używanej przez proces.
ivanleoncz
0

Po prostu miałem to samo, gdy próbowałem uruchomić skrypt Pythona z folderu współdzielonego w VirtualBoxnowym Ubuntu 20.04 LTS. Python wyłączył się Killedpodczas ładowania mojej własnej biblioteki. Kiedy przeniosłem folder do katalogu lokalnego, problem zniknął. Wygląda na to, że Killedzatrzymanie nastąpiło podczas początkowego importu mojej biblioteki, ponieważ po przeniesieniu folderu otrzymałem komunikaty o brakujących bibliotekach.

Problem zniknął po ponownym uruchomieniu komputera.

Dlatego ludzie mogą chcieć spróbować przenieść program do katalogu lokalnego, jeśli jest to jakiś udział lub może to być przejściowy problem, który wymaga tylko ponownego uruchomienia systemu operacyjnego.

Timothy C. Quinn
źródło
Czekaj, musiałeś zrestartować hosta lub maszynę wirtualną?
cglacet
Tak. W moim przypadku budowałem nową maszynę wirtualną i właśnie zainstalowałem Pythona, gdy zobaczyłem ten problem. Po ponownym uruchomieniu zniknął. Nienawidzę ponownego uruchamiania jako sposobu naprawiania rzeczy, więc spędziłem sporo czasu próbując debugować i po godzinie kopania, w tym tutaj w SO. Ale ostatecznie poddałem się, zrestartowałem i presto. Nie mam pojęcia, dlaczego zadziałało.
Timothy C. Quinn