Z technicznego punktu widzenia, dlaczego procesy w Erlangu są bardziej wydajne niż wątki systemu operacyjnego?

170

Charakterystyka Erlanga

Z Erlang Programming (2009):

Współbieżność Erlang jest szybka i skalowalna. Jego procesy są lekkie, ponieważ maszyna wirtualna Erlang nie tworzy wątku systemu operacyjnego dla każdego utworzonego procesu. Są tworzone, planowane i obsługiwane na maszynie wirtualnej niezależnie od bazowego systemu operacyjnego. W rezultacie czas tworzenia procesu jest rzędu mikrosekund i jest niezależny od liczby równolegle istniejących procesów. Porównaj to z Javą i C #, gdzie dla każdego procesu tworzony jest podstawowy wątek systemu operacyjnego: otrzymasz bardzo konkurencyjne porównania, z Erlangiem znacznie przewyższającym oba języki.

Z programowania współbieżnego w Erlang (pdf) (slajdy) (2003):

Obserwujemy, że czas potrzebny do stworzenia procesu Erlanga jest stały 1 µs do 2500 procesów; następnie wzrasta do około 3 µs do 30 000 procesów. Wydajność Java i C # jest pokazana u góry rysunku. W przypadku niewielkiej liczby procesów utworzenie procesu zajmuje około 300 µs. Stworzenie ponad dwóch tysięcy procesów jest niemożliwe.

Widzimy, że dla maksymalnie 30 000 procesów czas wysłania wiadomości między dwoma procesami Erlanga wynosi około 0,8 µs. W przypadku C # zajmuje to około 50 µs na wiadomość, aż do maksymalnej liczby procesów (czyli około 1800 procesów). Java była jeszcze gorsza, do 100 procesów zajmowało około 50 µs na wiadomość, po czym gwałtownie wzrosła do 10 ms na wiadomość, gdy było około 1000 procesów Java.

Moje myśli

Nie do końca rozumiem technicznie, dlaczego procesy Erlanga są o wiele bardziej wydajne w tworzeniu nowych procesów i mają znacznie mniejsze zużycie pamięci na proces. Zarówno system operacyjny, jak i maszyna wirtualna Erlang muszą wykonywać harmonogramy, przełączać konteksty, śledzić wartości w rejestrach i tak dalej ...

Dlaczego po prostu wątki systemu operacyjnego nie są implementowane w taki sam sposób, jak procesy w Erlang? Czy muszą wspierać coś więcej? Dlaczego potrzebują większej ilości pamięci? I dlaczego mają wolniejsze tarło i komunikację?

Technicznie, dlaczego procesy w Erlangu są wydajniejsze niż wątki systemu operacyjnego, jeśli chodzi o tworzenie i komunikację? I dlaczego nie można wdrażać i zarządzać wątkami w systemie operacyjnym w ten sam skuteczny sposób? I dlaczego wątki systemu operacyjnego mają większy ślad pamięci oraz wolniejsze tworzenie i komunikację?

Więcej czytania

Jonas
źródło
1
Zanim spróbujesz zrozumieć, dlaczego hipoteza jest prawdziwa, musisz ustalić, czy jest ona prawdziwa - np. Poparta dowodami. Czy masz odniesienia do porównań podobnych do podobnych, które pokazują, że proces Erlang jest w rzeczywistości bardziej wydajny niż (powiedzmy) wątek Java na aktualnej JVM? A może aplikacja w C korzystająca bezpośrednio z procesu systemu operacyjnego i obsługi wątków? (To drugie wydaje mi się bardzo, bardzo mało prawdopodobne. Pierwsze tylko trochę prawdopodobne). Mam na myśli, w wystarczająco ograniczonym środowisku (punkt Francisco), może to być prawda, ale chciałbym zobaczyć liczby.
TJ Crowder
1
@Donal: Tak jak w przypadku wielu innych bezwzględnych stwierdzeń. :-)
TJ Crowder
1
@Jonas: Dzięki, ale dotarłem do daty (02.11.1998) i wersji JVM (1.1.6) i zatrzymałem się. JVM firmy Sun poprawiło się nieco w ciągu ostatnich 11,5 lat (i prawdopodobnie tłumacz Erlanga również), szczególnie w obszarze gwintowania. (Żeby było jasne, nie twierdzę, że ta hipoteza nie jest prawdziwa [a Francisco i Donal wskazali, dlaczego Erland może być w stanie coś tam zrobić]; Mówię, że nie należy tego przyjmować za dobrą monetę bez sprawdzania.)
TJ Crowder
1
@Jonas: "... ale myślę, że możesz to zrobić w Erlang ..." To ta część "zgadywania", stary. :-) Zgadujesz, że przełączanie procesów Erlanga przekroczyło tysiące. Jesteś zgadywać , że robi to lepiej niż Java lub OS wątkach. Zgadywanie i tworzenie oprogramowania nie są dobrym połączeniem. :-) Ale myślę, że przedstawiłem swój punkt widzenia.
TJ Crowder
17
@TJ Crowder: Zainstaluj erlang i uruchom, erl +P 1000100 +hms 100a następnie wpisz {_, PIDs} = timer:tc(lists,map,[fun(_)->spawn(fun()->receive stop -> ok end end) end, lists:seq(1,1000000)]).i poczekaj około trzech minut na wynik. To takie proste. Zajmuje 140us na proces i 1 GB całej pamięci RAM na moim laptopie. Ale jest to bezpośrednio z powłoki, powinno być lepsze od skompilowanego kodu.
Hynek -Pichi- Vychodil

Odpowiedzi:

113

Istnieje kilka czynników przyczyniających się do tego:

  1. Procesy Erlang nie są procesami systemu operacyjnego. Są one implementowane przez maszynę wirtualną Erlang przy użyciu lekkiego modelu kooperacyjnego wątkowania (wywłaszczającego na poziomie Erlanga, ale pod kontrolą wspólnie zaplanowanego środowiska wykonawczego). Oznacza to, że przełączanie kontekstu jest znacznie tańsze, ponieważ przełączają się tylko w znanych, kontrolowanych punktach i dlatego nie muszą zapisywać całego stanu procesora (rejestry normalne, rejestry SSE i FPU, mapowanie przestrzeni adresowej itp.).
  2. Procesy Erlang używają dynamicznie alokowanych stosów, które na początku są bardzo małe i rosną w razie potrzeby. Pozwala to na powstanie wielu tysięcy - a nawet milionów - procesów Erlang bez pochłaniania całej dostępnej pamięci RAM.
  3. Erlang był kiedyś jednowątkowy, co oznacza, że ​​nie było wymogu zapewnienia bezpieczeństwa wątków między procesami. Obsługuje teraz SMP, ale interakcja między procesami Erlang w tym samym harmonogramie / rdzeniu jest nadal bardzo lekka (istnieją oddzielne kolejki uruchomień na rdzeń).
Marcelo Cantos
źródło
6
Do twojego drugiego punktu: A jeśli proces jeszcze się nie uruchomił, nie ma powodu, aby przypisywać do niego stos. Ponadto: można wykonać kilka sztuczek, majstrując przy GC procesu tak, aby nigdy nie zbierał pamięci. Ale to jest zaawansowane i nieco niebezpieczne :)
DAWAM ODPOWIEDZI NA CRAP
3
Do trzeciego punktu: Erlang wymusza niezmienne dane, więc wprowadzenie SMP nie powinno wpłynąć na bezpieczeństwo wątków.
nilskp
@ nilskp, zgadza się, erlang jest również funkcjonalnym językiem programowania, więc nie ma „zmiennych” danych. Prowadzi to do bezpieczeństwa wątków.
liuyang1
6
@nilskp: (RE: skomentujesz punkt 3…) Mimo że sam język ma niezmienny system typów, jego podstawowa implementacja - przekazywanie wiadomości, harmonogram itp. - to zupełnie inna historia. Prawidłowe i wydajne wsparcie SMP nie odbywa się po prostu za jednym pstryknięciem przełącznika.
Marcelo Cantos
@rvirding: Dzięki za doprecyzowanie. Pozwoliłem sobie włączyć twoje punkty do treści mojej odpowiedzi.
Marcelo Cantos
73

Po dalszych poszukiwaniach znalazłem prezentację autorstwa Joe Armstronga.

Z Erlang - oprogramowanie do współbieżnego świata (prezentacja) (po 13 min):

[Erlang] jest językiem współbieżnym - rozumiem przez to, że wątki są częścią języka programowania, nie należą do systemu operacyjnego. To naprawdę jest nie tak w językach programowania, takich jak Java i C ++. Jego wątki nie są w języku programowania, wątki są czymś w systemie operacyjnym - i dziedziczą wszystkie problemy, które mają w systemie operacyjnym. Jednym z problemów jest ziarnistość systemu zarządzania pamięcią. Zarządzanie pamięcią w systemie operacyjnym chroni całe strony pamięci, więc najmniejszy rozmiar, jaki może mieć wątek, to najmniejszy rozmiar strony. To właściwie za duże.

Jeśli dodasz więcej pamięci do swojej maszyny - masz taką samą liczbę bitów, która chroni pamięć, więc ziarnistość tablic stron rośnie - w końcu używasz powiedzmy 64kB dla procesu, który znasz, działający w kilkuset bajtach.

Myślę, że odpowiada, jeśli nie na wszystkie, przynajmniej na kilka moich pytań

Jonas
źródło
2
Ochrona pamięci na stosach nie bez powodu. Czy Erlang po prostu nie chroni stosów różnych kontekstów wykonywania za pośrednictwem jednostki MMU procesora? (I po prostu miej nadzieję na najlepsze?) A co, jeśli wątek zużywa więcej niż mały stos? (Czy wszystkie alokacje stosów są sprawdzane, aby zobaczyć, czy potrzebny jest większy stos? Czy stos można przenosić?)
Thanatos
2
@Thanatos: Erlang nie pozwala programom na dostęp do pamięci lub manipulowanie stosem. Wszystkie alokacje muszą przejść przez zarządzane środowisko wykonawcze, zarówno stertę, jak i stos. Innymi słowy: ochrona sprzętu jest bezużyteczna, ponieważ chroni przed rzeczami, które i tak nie mogą się wydarzyć. Język jest bezpieczny dla wskaźnika, bezpieczny dla stosu, bezpieczny dla pamięci i bezpieczny dla typów. Proces nie może zużywać więcej niż „malutki stos”, ponieważ stos rośnie w miarę potrzeb. Możesz myśleć o tym jako o przeciwieństwie malutkiego: nieskończenie duży. (Ale leniwie przydzielone.)
Jörg W Mittag
4
Powinieneś spojrzeć na Singularity Operating System firmy Microsoft Research. W Singularity cały kod, jądro, sterowniki urządzeń, biblioteki i programy użytkownika działają w pierścieniu 0 z pełnymi uprawnieniami jądra. Cały kod, jądro, sterowniki urządzeń, biblioteki i programy użytkownika działają w jednej płaskiej fizycznej przestrzeni adresowej bez jakiejkolwiek ochrony pamięci. Zespół odkrył, że gwarancje, jakie daje język, są znacznie silniejsze niż gwarancje, które może dać MMU, a jednocześnie użycie MMU kosztowało ich do 30% (!!!) wydajności. Więc po co używać MMU, skoro twój język już to robi?
Jörg W Mittag,
1
System operacyjny OS / 400 działa w ten sam sposób. Dla wszystkich programów istnieje tylko jedna płaska przestrzeń adresowa. Większość aktualnie używanych języków ma te same właściwości bezpieczeństwa (ECMAScript, Java, C♯, VB.NET, PHP, Perl, Python, Ruby, Clojure, Scala, Kotlin, Groovy, Ceylon, F♯, OCaml, „Celowa” część „Celu-C”, „++” część „C ++”). Gdyby nie starszy kod C i starsze funkcje C ++ i Objective-C, nie potrzebowalibyśmy już nawet pamięci wirtualnej.
Jörg W Mittag,
47

Zaimplementowałem programy w asemblerze i zmierzyłem wydajność.

Przełączanie między programami, zwanymi procesami Erlanga, zajmuje około 16 instrukcji i 20 nanosekund na nowoczesnym procesorze. Ponadto często znasz proces, na który się przełączasz (przykład: proces odbierający wiadomość w swojej kolejce może być zaimplementowany jako bezpośrednie przekazanie z procesu wywołującego do procesu odbierającego), więc harmonogram nie wchodzi w grę, jest to operacja O (1).

Zmiana wątków systemu operacyjnego zajmuje około 500-1000 nanosekund, ponieważ wywołujesz jądro. Harmonogram wątków systemu operacyjnego może działać w czasie O (log (n)) lub O (log (log (n))), co zacznie być zauważalne, jeśli masz dziesiątki tysięcy, a nawet miliony wątków.

Dlatego procesy Erlang są szybsze i lepiej skalowalne, ponieważ zarówno podstawowa operacja przełączania jest szybsza, jak i program planujący działa rzadziej.

Surfer Jeff
źródło
33

Procesy Erlang odpowiadają (w przybliżeniu) zielonym wątkom w innych językach; nie ma wymuszonej przez system operacyjny separacji między procesami. (Może istnieć separacja wymuszona językiem, ale jest to mniejsza ochrona, mimo że Erlang wykonuje lepszą pracę niż większość.) Ponieważ są o wiele lżejsze, mogą być używane znacznie szerzej.

Z drugiej strony wątki systemu operacyjnego można po prostu zaplanować na różnych rdzeniach procesora i (w większości) są w stanie obsługiwać niezależne przetwarzanie związane z procesorem. Procesy systemu operacyjnego są podobne do wątków systemu operacyjnego, ale z dużo silniejszą separacją wymuszaną przez system operacyjny. Ceną tych możliwości jest to, że wątki systemu operacyjnego i (a tym bardziej) procesy są droższe.


Innym sposobem zrozumienia różnicy jest to. Przypuśćmy, że zamierzasz napisać implementację Erlanga w górnej części JVM (nie jest to szczególnie szalona sugestia), wtedy każdy proces Erlanga będzie obiektem z pewnym stanem. Miałbyś wtedy pulę wystąpień Thread (zwykle o rozmiarze odpowiadającym liczbie rdzeni w systemie hosta; to jest dostrajalny parametr w rzeczywistych środowiskach wykonawczych Erlang), które uruchamiają procesy Erlang. To z kolei spowoduje rozłożenie pracy, która ma zostać wykonana, na rzeczywiste dostępne zasoby systemowe. To całkiem zgrabny sposób robienia rzeczy, ale całkowicie polegafakt, że każdy indywidualny proces Erlanga nie robi wiele. To jest w porządku, oczywiście; Erlang jest tak skonstruowany, że nie wymaga, aby te indywidualne procesy były ciężkie, ponieważ to cały ich zespół wykonuje program.

Pod wieloma względami prawdziwym problemem jest terminologia. Rzeczy, które Erlang nazywa procesami (i które silnie korespondują z tą samą koncepcją w CSP, CCS, a zwłaszcza w rachunku π), po prostu nie są tym samym, co języki z dziedzictwem C (w tym C ++, Java, C # i wiele innych) wywołują proces lub wątek. Istnieją pewne podobieństwa (wszystkie wiążą się z pojęciem równoczesnego wykonywania), ale na pewno nie ma równoważności. Dlatego bądź ostrożny, gdy ktoś mówi do ciebie „proces”; mogą zrozumieć, że oznacza to coś zupełnie innego…

Donal Fellows
źródło
3
Erlang nie zbliża się nigdzie do rachunku Pi Calculus. Rachunek Pi zakłada synchroniczne zdarzenia w kanałach, które można powiązać ze zmiennymi. Ten rodzaj koncepcji w ogóle nie pasuje do modelu Erlanga. Spróbuj dołączyć do Calculusa, Erlang jest bliżej tego, chociaż nadal musi mieć możliwość natywnego dołączania do niektórych wiadomości i tak dalej. Była praca dyplomowa (i projekt) o nazwie JErlang, która ją wdrożyła.
UDZIELAM STRASZNYCH PORAD
Wszystko zależy od tego, co dokładnie widzisz w rachunku rachunku pi (i możesz modelować kanały asynchroniczne z kanałami synchronicznymi i procesami buforowymi).
Donal Fellows
Mówisz tylko, że procesy Erlang są lekkie, ale nie wyjaśniasz, dlaczego mają mniejszy ślad (są lekkie) i dlaczego mają lepszą wydajność niż wątki systemu operacyjnego.
Jonas
1
@Jonas: W przypadku niektórych typów zadań (szczególnie zadań wymagających dużej mocy obliczeniowej) wątki systemu operacyjnego działają lepiej. Pamiętaj, że nie są to typowe zadania, do których używany jest Erlang; Erlang koncentruje się na dużej liczbie prostych zadań komunikacyjnych. Jedną z korzyści płynących z tego jest to, że w przypadku grupy zadań, które wykonują pracę i czekają na wynik, wszystko to można wykonać w jednym wątku systemu operacyjnego na jednym procesorze, co jest bardziej wydajne niż posiadające przełączniki kontekstowe.
Donal Fellows
Teoretycznie możesz sprawić, że wątek systemu operacyjnego będzie również bardzo tani, używając bardzo małego stosu i uważnie kontrolując liczbę innych przydzielonych zasobów specyficznych dla wątku, ale w praktyce jest to dość problematyczne. (Przewidywanie wymagań dotyczących stosu jest trochę czarną sztuką). Zamiast tego wątki systemu operacyjnego są szczególnie zaprojektowane tak, aby były optymalne w przypadku, gdy jest ich mniej (rzędu liczby rdzeni procesora) i gdzie robią większe ilości przetwarzania każdy.
Donal Fellows
3

Myślę, że Jonas chciał uzyskać kilka liczb dotyczących porównania wątków systemu operacyjnego z procesami Erlanga. Autor programu Programming Erlang, Joe Armstrong, jakiś czas temu testował skalowalność tworzenia procesów Erlang w wątkach systemu operacyjnego. Napisał prosty serwer WWW w Erlang i przetestował go na wielowątkowym Apache (ponieważ Apache używa wątków OS). Istnieje stara strona internetowa z danymi sięgającymi 1998 roku. Tylko raz udało mi się znaleźć tę witrynę. Więc nie mogę podać linku. Ale informacje tam są. Główny punkt badania wykazał, że Apache osiągnął maksymalny poziom prawie 8K procesów, podczas gdy jego ręcznie napisany serwer Erlang obsłużył ponad 10 000 procesów.

Jurnell
źródło
5
Myślę, że mówisz o tym: sics.se/~joe/apachevsyaws.html Ale zapytałem, jak erlang sprawia, że ​​wątki są tak wydajne w porównaniu z wątkami kerlenl.
Jonas
Link @Jonas nie działa. Ostatnia migawka jest tutaj
alvaro g
1
W artykule napisano: „Apache umiera po około 4 000 równoległych sesjach. Yaws nadal działa przy ponad 80 000 równoległych połączeniach”.
Nathan Long
zobacz cały artykuł na citeseerx.ist.psu.edu/viewdoc/ ... Rzeczywiście, złamanie serwera Erlang przy użyciu 16 atakujących maszyn okazało się niemożliwe - chociaż łatwo było zatrzymać serwer Apache.
Bernhard
1

Ponieważ tłumacz Erlang musi martwić się tylko o siebie, system operacyjny ma wiele innych powodów do zmartwień.

Francisco Soto
źródło
0

Jednym z powodów jest to, że proces erlang jest tworzony nie w systemie operacyjnym, ale w evm (wirtualna maszyna erlang), więc koszt jest mniejszy.

szczur
źródło