Co powoduje błąd segmentacji w Pythonie?

85

Implementuję algorytm przeszukiwania wykresów Strong Connected Component (SCC) Kosaraju w Pythonie.

Program działa świetnie na małym zestawie danych, ale kiedy uruchamiam go na bardzo dużym wykresie (ponad 800 000 węzłów), wyświetla komunikat „Błąd segmentacji”.

Jaka może być tego przyczyna? Dziękuję Ci!


Dodatkowe informacje: Najpierw otrzymałem ten błąd podczas pracy na bardzo dużym zestawie danych:

"RuntimeError: maximum recursion depth exceeded in cmp"

Następnie zresetowałem limit rekursji za pomocą

sys.setrecursionlimit(50000)

ale otrzymałem „błąd segmentacji”

Uwierz mi, to nie jest nieskończona pętla, działa poprawnie na stosunkowo mniejszych danych. Czy możliwe, że program wyczerpał zasoby?

xiaolong
źródło
10
Może możesz zajrzeć CrashingPython
Abhijit
2
Czy to działa w czystym Pythonie, czy używasz modułu rozszerzenia C? Jeśli jest to czysty Python, to jest tam błąd i gratulacje. Jeśli używasz modułu AC, prawdopodobnie stamtąd pochodzi segfault.
aaronasterling
to czysty Python. Program działa świetnie na stosunkowo niewielkim zbiorze danych i sprawiło, że pomyślałem, że kod jest poprawny.
xiaolong
Według dokumentacji Pythona:
James Thiele,
2
Zgodnie z dokumentacją Pythona :::::: Najwyższy możliwy limit zależy od platformy. Użytkownik może potrzebować ustawić wyższy limit, gdy ma program, który wymaga głębokiej rekurencji i platformę obsługującą wyższy limit. Należy to zrobić ostrożnie, ponieważ zbyt wysoki limit może doprowadzić do awarii. :::::: Nie określono systemu operacyjnego. Odniesienie do awarii może oznaczać błąd segmentacji w systemie operacyjnym. Spróbuj użyć mniejszego stosu. Ale IIRC, algorytm, którego używasz, umieszcza rntire SSC na stosie, więc możesz zabraknąć stosu.
James Thiele,

Odpowiedzi:

80

Dzieje się tak, gdy rozszerzenie Pythona (napisane w C) próbuje uzyskać dostęp do pamięci poza zasięgiem.

Możesz to prześledzić na następujące sposoby.

  • Dodaj sys.settracew pierwszym wierszu kodu.
  • Użyj w gdbsposób opisany przez Marka w tej odpowiedzi .. W wierszu polecenia

    gdb python
    (gdb) run /path/to/script.py
    ## wait for segfault ##
    (gdb) backtrace
    ## stack trace of the c code
    
Shiplu Mokaddim
źródło
2
dzięki, ale mój kod to czysty Python, czy to robi różnicę?
xiaolong
Sprawdź, których modułów Pythona używasz? Niektóre moduły są napisane w Pythonie, a inne w C. Myślę, że musisz zgłosić błąd.
Shiplu Mokaddim,
1
podobne, również pomocne: moduł śledzenia stdlib właśnie pomógł mi dotrzeć do sedna błędu segmentacji na serwerze pomostowym, bez instalowania nowej zależności i bez modyfikowania kodu.
driftcatcher
4
na OSX Sierra gdb został zastąpiony przez lldb
kthouz
54

Rozumiem, że rozwiązałeś swój problem, ale dla innych czytających ten wątek odpowiedź jest następująca: musisz zwiększyć stos, który Twój system operacyjny przydziela dla procesu Pythona.

Sposób na to zależy od systemu operacyjnego. W Linuksie możesz sprawdzić za pomocą polecenia ulimit -sswoją aktualną wartość i możesz ją zwiększyć za pomocąulimit -s <new_value>

Spróbuj podwoić poprzednią wartość i kontynuuj podwajanie, jeśli to nie zadziała, aż znajdziesz taką, która działa lub zabraknie jej pamięci.

Davide
źródło
Również dobrym sposobem sprawdzenia, czy nie zbliżasz się do ulimit max, jest bieganie lsofi używanie greplub wc -lśledzenie wszystkiego.
wydano
Zgadzam się. To faktycznie zadziałało w przypadku implementacji SCC w moim Kosaraju, naprawiając błąd segfault w implementacjach Pythona i C ++. <br/> W przypadku mojego MAC, dowiedziałem się o możliwym maksimum poprzez:
Rock
4
zwróć uwagę, że wartość ulimit jest modyfikowana tylko dla konkretnej powłoki, w której jest wykonywany, aby nie przypadkowo zmodyfikować wartość dla całego systemu
Tanmay Garg
1
Zrobiłem to i skończyło się na ulimit -s 16384, jednak po uruchomieniu nadal otrzymywałem błąd segmentacji.
Sreehari R
@SreehariR Spróbuj jeszcze bardziej ją zwiększyć. Jednak może to być również problem z rozszerzeniem Pythona (jeśli go używasz), które (ta inna odpowiedź) [ stackoverflow.com/a/10035594/25891] sugeruje, jak debugować
Davide
18

Błąd segmentacji ma charakter ogólny, istnieje wiele możliwych przyczyn:

  • Słaba pamięć
  • Wadliwa pamięć RAM
  • Pobieranie ogromnego zestawu danych z bazy danych za pomocą zapytania (jeśli rozmiar pobranych danych jest większy niż pamięć wymiany)
  • zły kod zapytania / błędny kod
  • posiadanie długiej pętli (wielokrotna rekurencja)
Sadheesh
źródło
3

Aktualizacja ulimit zadziałała dla mojej implementacji SCC w Kosaraju, naprawiając błąd segfault w implementacjach Pythona (Python segfault… kto wiedział!) I C ++.

W przypadku mojego MAC znalazłem możliwe maksimum poprzez:

$ ulimit -s -H
65532
Skała
źródło
jak zaktualizować tę wartość? czy to wartość w jakim typie jednostki?
Pablo
Jeśli WIESZ dużo o tym, jakie są Twoje ograniczenia (i wiesz, że Twoja platforma nigdy nie odejdzie od Linuksa), możesz użyć polecenia wykonywania w języku Python, aby po prostu wykonać to polecenie z kodu. Osobiście dodałem go do mojego pliku .bashrc.
trąbki
2

Wyszukiwarka Google znalazła ten artykuł i nie widziałem omówionego następującego „rozwiązania osobistego”.


Moją ostatnią irytacją związaną z Pythonem 3.7 w podsystemie Windows dla systemu Linux jest to, że: na dwóch komputerach z tą samą biblioteką Pandas, jeden daje mi segmentation faultostrzeżenie, a drugi zgłasza. Nie było jasne, który z nich jest nowszy, ale „ponowna instalacja” pandasrozwiązuje problem.

Rozkaz, że uruchomiłem na buggy machine.

conda install pandas

Więcej szczegółów: uruchamiałem identyczne skrypty (zsynchronizowane przez Git) i oba są maszynami Windows 10 z WSL + Anaconda. Oto zrzuty ekranu, aby złożyć sprawę. Ponadto na komputerze, na który pythonbędzie narzekać wiersz poleceń Segmentation fault (core dumped), laboratorium Jupyter po prostu restartuje jądro za każdym razem. Co gorsza, nie było żadnego ostrzeżenia.

wprowadź opis obrazu tutaj


Aktualizacje kilka miesięcy później: przestałem hostować serwery Jupyter na komputerze z systemem Windows. Teraz używam WSL w systemie Windows do pobierania zdalnych portów otwartych na serwerze Linux i uruchamiania wszystkich moich zadań na zdalnym komputerze z systemem Linux. Nigdy nie doświadczyłem żadnego błędu wykonania przez dobrą liczbę miesięcy :)

llinfeng
źródło
0

Wystąpił ten błąd segmentacji po uaktualnieniu dlib na RPI. Prześledziłem stos, jak zasugerował Shiplu Mokaddim powyżej, i ustaliłem na podstawie biblioteki OpenBLAS.

Ponieważ OpenBLAS jest również wielowątkowy, użycie go w aplikacji wielowątkowej spowoduje wykładnicze mnożenie wątków aż do błędu segmentacji. W przypadku aplikacji wielowątkowych ustaw OpenBlas na tryb jednowątkowy.

W środowisku wirtualnym Pythona powiedz OpenBLAS, aby używał tylko jednego wątku, edytując:

    $ workon <myenv>
    $ nano .virtualenv/<myenv>/bin/postactivate

i dodaj:

    export OPENBLAS_NUM_THREADS=1 
    export OPENBLAS_MAIN_FREE=1

Po ponownym uruchomieniu mogłem uruchomić wszystkie aplikacje do rozpoznawania obrazu na rpi3b, które wcześniej go zawieszały.

źródło: https://github.com/ageitgey/face_recognition/issues/294

Digitalf8
źródło
-1

Wygląda na to, że zabrakło pamięci stosu. Możesz chcieć go zwiększyć, jak stwierdził Davide. Aby to zrobić w kodzie Pythona, musiałbyś uruchomić swoją "main ()" używając wątków:

def main():
    pass # write your code here

sys.setrecursionlimit(2097152)    # adjust numbers
threading.stack_size(134217728)   # for your needs

main_thread = threading.Thread(target=main)
main_thread.start()
main_thread.join()

Źródło: post c1729 o codeforsces . Uruchomienie go z PyPy jest nieco trudniejsze .

Rustam A.
źródło