Erlang vs Go vs Scala

Odpowiedz Nowy wątek
2018-06-28 20:47
0

Natrafiłem ostatnio na mały research dotyczący w.w. języków w kwestii skalowalności i szerokorozumianego performance (latency i throughput)

Link: http://www.dcs.gla.ac.uk/~trinder/papers/sac-18.pdf

TL;DR (Conclusion):

We have investigated programming language characteristics that
support the engineering of multicore web servers. Crucially these
languages must be able to support massive concurrency on multicore
machines with low communication and synchronisation overheads.
We have analysed 12 languages considering computation,
coordination, compilation, and popularity, and selected three representative
high-level languages for detailed analysis: Erlang, Go,
and Scala/Akka (Section 2). We have designed three server benchmarks
that analyse key performance characteristics of the languages,
i.e. inter-process communication latency, process creation time, the
maximum number of supported processes, and throughput (Section
3).
A summary of the recommendations based on this small set of
benchmarks is as follows. For a server where minimising message
latency is crucial, Go and Erlang are the best choice (Figure 3).
Interestingly Akka significantly reduces communication latency in
Scala (Figures 3(a) and 3(b)). Scala/Akka are capable of maintaining
the largest number of dormant processes (∼11M processes in
Figure 4(a)), while Erlang performs the best when processes are
short lived and the goal is to ensure minimal spawn time, e.g. Erlang
takes 58s to spawn 9M processes (Section 4.2). In server applications
where up to 100,000 processes are frequently spawned, Erlang and
Go minimise process creation time and scale smoothly (Figure 4(b)).
Experiments with communicating pairs of processes show that Go
provides the highest throughput independent of the number of cores
and the number of process pairs (Figure 5).
Comparing the performance of complete case study servers implemented
in each language would significantly reinforce these
results, and one possibility is an Instant Messaging (IM) benchmark
[4]. It would also be interesting to study the performance
overheads of providing fault tolerance and of recovering from faults,
another key server capability. Finally we could compare the performance
of server languages on distributed memory architectures, e.g.
a cluster of multicores.

Pozostało 580 znaków

2018-06-28 21:01
0

Erlang takes 58s to spawn 9M processes (Section 4.2). In server applications where up to 100,000 processes are frequently spawned, Erlang and Go minimise process creation time and scale smoothly

Co to jest proces w Go i która apka w Go odpala często setki tysięcy procesów? W ogóle kto tak robi?


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit, 2018-06-28 21:03
Tak goroutines. Kto tak robi? Duze korpo i koncerny telekomunikacyjne. Średni jest sens programować asynchronicznie stronki firmowe, ale serwery komunikacyjne, gier, serwery reklam itp na tym bardzo korzystają. - siloam 2018-07-03 22:15

Pozostało 580 znaków

2018-06-28 21:16
0
Wibowit napisał(a):

Erlang takes 58s to spawn 9M processes (Section 4.2). In server applications where up to 100,000 processes are frequently spawned, Erlang and Go minimise process creation time and scale smoothly

Co to jest proces w Go i która apka w Go odpala często setki tysięcy procesów? W ogóle kto tak robi?

Słuszne spostrzeżenie, z tego co widzę (źródła wrzucili tutaj: https://github.com/bbstk/server-languages-benchmarks/) pod pojęciem "procesu" w Go oni rozumują goroutines - to nie są procesy, a wątki w go. Tych wątków można rzeczywiscie spawnować duże ilości ze względu na to, że to nie są wątki 1:1 w OS. Czyli generalnie, pod pojęciem "processes", w tym dokumencie (wnioskuje po kodzie w Go i Erlangu, Scali nie znam) rozumują wątki.

Jup. W Go można sobie natrzaskać wirtualnych wątków. Miałem dzieś nawet fajną prezentację na ten temat. Jak ktoś by chciał to postaram się odnaleźć. - theacid 2018-06-28 22:09

Pozostało 580 znaków

2018-06-28 21:35
1

@TurkucPodjadek: w Erlangu to się nazywa procesami, ale nie są to procesy systemowe a Erlangowe właśnie i @Wibowit w Erlangu się je spawnuje na potęgę, przykładowo w Cowboyu (chyba najpopularniejszym serwerze HTTP Erlangowym) każde połączenie przychodzące to jeden niezależny proces, dzięki czemu niezłapany wyjątek nie wyłoży Ci połowy aplikacji a zwyczajnie w świecie ubije jeden proces i po sprawie (bo tak się to najzwyczajniej w Erlangu robi).

Ogólnie kiedyś znalazłem info (teraz nie mogę go ponownie znaleźć), że Akka była inspirowana Erlangiem i autor zwyczajnie chciał przenieść Erlangowe abstrakcje do świata JVMa. Z tego co wiem (nie używałem Akki) podobno wyszło nie najgorzej.

Co do skalowalności to automatyczna klasteryzacja w Erlangu (która jest idiotycznie prosta, bo w większości sprowadza się do net_kernel:connect_node('[email protected]') i działa) IMHO wymiata większość tego co jest dostępne. Może sam język nie jest demonem wydajności (JIT bazujący na LLVM jest w trakcie rozwoju), ale dzięki wielu pomysłom (np. GC per proces [wątek jeśli wolicie tą nazwę]) nie traci aż tak bardzo w starciu z obecnymi platformami. Dodatkowo ostatnio pojawia się sporo języków (Elixir, LFE, Joxa), które używają tej samej platformy i oferują "przystępniejszą" składnię niż oryginalna prologowa.

Pozostało 580 znaków

2018-06-28 22:05
0

każde połączenie przychodzące to jeden niezależny proces, dzięki czemu niezłapany wyjątek nie wyłoży Ci połowy aplikacji a zwyczajnie w świecie ubije jeden proces i po sprawie (bo tak się to najzwyczajniej w Erlangu robi).

W każdym typowym JVMowym frameworku wyjątek przy obsłudze żądania nie wywala aplikacji, więc ubijanie pseudo-procesu w tej kwestii nic nie zmienia.

Słuszne spostrzeżenie, z tego co widzę (źródła wrzucili tutaj: https://github.com/bbstk/server-languages-benchmarks/) pod pojęciem "procesu" w Go oni rozumują goroutines - to nie są procesy, a wątki w go.

Goroutines to nie są wątki - goroutines to bardziej async/ await z dodatkową komunikacją.

Kotlin ma coroutines: https://kotlinlang.org/docs/reference/coroutines.html - prawdopodobnie da się na nich uzyskać coś podobnego do Golangowych goroutines. Jak na razie są eksperymentalne, ale i tak obiecujące.

Wątek natywny potrzebuje zawsze stosu, więc odpalenie milionów natywnych wątków rozsadzi cały RAM i apka leży razem z całym systemem (w sensie OS może i nie leży, ale wpada w panikę i ubija procesy losowo). Wszelakie pseudo procesy czy wątki, które można uruchamiać w ilościach rzędu setki tysięcy naraz są wariantami zielonych wątków: https://en.wikipedia.org/wiki/Green_threads

Ogólnie kiedyś znalazłem info (teraz nie mogę go ponownie znaleźć), że Akka była inspirowana Erlangiem i autor zwyczajnie chciał przenieść Erlangowe abstrakcje do świata JVMa. Z tego co wiem (nie używałem Akki) podobno wyszło nie najgorzej.

Erlang jest przykładem modelu aktorowego, który sprawdził się w praktyce, więc Akka się na nim wzorowała. Jednak od czasów powstania Akka idzie własną drogą i przenoszenie żywcem rozwiązań z Erlanga do Akki niekoniecznie jest optymalne. W świecie Akki jest teraz moda na Reactive Streams na których oparta jest Akka-HTTP. Aktory nie są przeznaczone do samego asynchronicznego przetwarzania wiadomości, a raczej do zarządzania stanem i hierarchią nadzoru. Aktory nie mają backpressure (które jest obecne w Reactive Streams), więc mają tendencję do wypieprzania całej JVMki z powodu braku pamięci do przetwarzania wiadomości przy zbyt dużym obciążeniu. Backpressure z Akka Streams pozwala natomiast na spowolnienie obsługiwania żądań do poziomu, który jest mozliwy w danej chwili - aplikacja przetwarza żądania z pełną prędkością, ale nie zaczyna przetwarzać nowych jeśli aktualnie nie ma wolnych zasobów. Przy przeciążeniu nie ma sensu bezrefleksyjnie zaczynać przetwarzania kolejnego żądania (każde kolejne żądanie przetwarzane w tym samym czasie co inne zwiększa zapotrzebowanie na RAM), a zamiast tego lepiej skupić się na przetworzeniu do końca już rozpoczętych żądań.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit, 2018-06-28 22:13

Pozostało 580 znaków

2018-06-28 22:38
0

@Wibowit: ale to jest od dawna stosowana praktyka, czy to poprzez supervisor z modelem simple_one_for_one (lub Elixirowy wrapper na to w postaci DynamicSupervisor od wersji 1.6) czy poprzez biblioteki jak poolboy, które używają wcześniej wymienionego mechanizmu. IIRC Cowboy również używa jakiegoś mechanizmu do back pressure (czy to poolboy czy coś innego nie pamiętam). Poza tym jakiś czas temu pojawiła się biblioteka GenStage dla Elixira, która oferuje podobne rozwiązanie. Wygląda również na to, że samo wysyłanie wiadomości jest obarczone mechanizmem backpressure (a przynajmniej było), więc to nie jest tak, że Erlangowy system "jest durny". Jednak te 9 lat różnicy między Erlangiem (1986) a Javą (1995) dają o sobie znać w takich momentach.

edytowany 1x, ostatnio: hauleth, 2018-11-07 18:21

Pozostało 580 znaków

2018-06-28 23:34
0

Wygląda również na to, że samo wysyłanie wiadomości jest obarczone mechanizmem backpressure (a przynajmniej było), więc to nie jest tak, że Erlangowy system "jest durny".

Właśnie z tej podlinkowanej wiadomości wynika, że jest trochę durny. Po pierwsze działa probabilitstycznie:

sending messages to load queues is very expensive and will lead the scheduler to context switch to another process more often

'more often' nie brzmi jak jednoznaczna gwarancja. Po drugie w przypadku nowego aktora per żądanie działa odwrotnie niż powinien:

Sending messages to processes with zero messages in the queue is free in terms of reductions

Nowy aktor będzie miał pustą kolejkę wiadomości, więc priorytetem będzie zaczynanie obsługi nowych żądań zamiast kończenie już zaczętych. To nie jest dobra strategia przy przeciążeniu systemu. No chyba, że czegoś nie zrozumiałem :)

Reactive streams działają zupełnie inaczej niż aktory jeśli spojrzeć na to z której strony inicjowane jest przetwarzanie wiadomości. W mechanizmie aktorów przetwarzanie nowych wiadomości odbywa się na skutek wrzucenia ich do początkowego aktora w łańcuchu przetwarzania (proste i logiczne). W mechanizmie reactive streams przetwarzanie nowych wiadomości odbywa się na skutek zasygnalizowania przez końcówkę strumienia gotowości otrzymania nowych wiadomości. Dokładnie to jest to rozszerzone na cały strumień - poprzedni element w strumieniu jest wyłączony dopóki następny element nie zasygnalizuje mu, że jest gotowy odebrać nowe elementy. Do tego dochodzą bufory pomiędzy elementami strumienia, by zminimalizować narzut na komunikację - bufory pozwalają na ukrycie opóźnień związanych z sygnalizacją gotowości na odebranie kolejnych porcji danych.

Backpressure w Akka Streams działa bardzo podobnie do flow-control w TCP/IP: https://en.wikipedia.org/wiki[...]_Protocol#/media/File:Tcp.svg
Można obejrzeć w akcji integrację Akka Streams z flow-control w TCP/IP tutaj:


Trzeba wziąć jeszcze pod uwagę, że Akka Streams są nieblokujące, więc to czekanie na zwolnienie bądź napełnienie się bufora TCP/ IP tak naprawdę nie blokuje żadnego wątku.
Akka Streams mają integrację nie tylko z TCP i HTTP(S), ale także z plikami ( https://doc.akka.io/docs/akka[...]eam-io.html#streaming-file-io ) i bazami danych ( http://slick.lightbend.com/ )


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit, 2018-06-28 23:39

Pozostało 580 znaków

2018-06-29 00:24
0
Wibowit napisał(a):

każde połączenie przychodzące to jeden niezależny proces, dzięki czemu niezłapany wyjątek nie wyłoży Ci połowy aplikacji a zwyczajnie w świecie ubije jeden proces i po sprawie (bo tak się to najzwyczajniej w Erlangu robi).

W każdym typowym JVMowym frameworku wyjątek przy obsłudze żądania nie wywala aplikacji, więc ubijanie pseudo-procesu w tej kwestii nic nie zmienia.

Słuszne spostrzeżenie, z tego co widzę (źródła wrzucili tutaj: https://github.com/bbstk/server-languages-benchmarks/) pod pojęciem "procesu" w Go oni rozumują goroutines - to nie są procesy, a wątki w go.

Goroutines to nie są wątki - goroutines to bardziej async/ await z dodatkową komunikacją.

Goroutines to takie wątki, ale w Go, ale nie są one oczywiście tożsame z wątkami w systemie operacyjnym (zdaje się o tym wspomniałem)
Tutaj jest wyjaśnione jak to działa pod spodem

Pozostało 580 znaków

2018-06-29 00:50
0

Goroutines to takie wątki, ale w Go, ale nie są one oczywiście tożsame z wątkami w systemie operacyjnym

No i rozmywa się nam pojęcie wątku :] Z samego artykułu który przytoczyłeś wynika iż scheduler w Go opiera się o cooperative multitasking, a nie o preemptive multitasking. Go nie wstrzyma działającej gorutyny o ile ta gorutyna nie wejdzie w jakąś metodę, która współpracuje ze schedulerem Go. Tymczasem wątki mogą być wywłaszczone w dowolnym momencie (preemption). Formą cooperative multitasking jest np: https://en.wikipedia.org/wiki/Fiber_(computer_science)

Lepiej skupić się na konkretnych problemach i ich rozwiązaniach, a nie na nazywaniu abstrakcji, które ciężko skategoryzować.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 3x, ostatnio: Wibowit, 2018-06-29 00:55

Pozostało 580 znaków

2018-06-29 12:03
0
Wibowit napisał(a):

Wygląda również na to, że samo wysyłanie wiadomości jest obarczone mechanizmem backpressure (a przynajmniej było), więc to nie jest tak, że Erlangowy system "jest durny".

Właśnie z tej podlinkowanej wiadomości wynika, że jest trochę durny. Po pierwsze działa probabilitstycznie:

sending messages to load queues is very expensive and will lead the scheduler to context switch to another process more often

'more often' nie brzmi jak jednoznaczna gwarancja. Po drugie w przypadku nowego aktora per żądanie działa odwrotnie niż powinien:

Sending messages to processes with zero messages in the queue is free in terms of reductions

Nowy aktor będzie miał pustą kolejkę wiadomości, więc priorytetem będzie zaczynanie obsługi nowych żądań zamiast kończenie już zaczętych. To nie jest dobra strategia przy przeciążeniu systemu. No chyba, że czegoś nie zrozumiałem :)

Każdy proces ma przypisane standardowo tyle samo redukcji (czasu procesora), więc źle to zrozumiałeś. To wysyłający zadania będzie miał zmniejszaną ilość redukcji, nie odbierający, więc dalej zadania będą przetwarzane tak jak było to zaplanowane. Domyślnie IIRC jest to 2000 redukcji zanim zostanie przełączony kontekst. Z racji, że w Erlangu każde zadanie z reguły to osobny proces, to w ten sposób "głodzisz" wysyłającego, a nie odbiorców.

W mechanizmie reactive streams przetwarzanie nowych wiadomości odbywa się na skutek zasygnalizowania przez końcówkę strumienia gotowości otrzymania nowych wiadomości. Dokładnie to jest to rozszerzone na cały strumień - poprzedni element w strumieniu jest wyłączony dopóki następny element nie zasygnalizuje mu, że jest gotowy odebrać nowe elementy.

Dokładnie tak działa gen_stage, gdzie to odbiorca ogranicza ile wiadomości chce otrzymać.

Trzeba wziąć jeszcze pod uwagę, że Akka Streams są nieblokujące, więc to czekanie na zwolnienie bądź napełnienie się bufora TCP/ IP tak naprawdę nie blokuje żadnego wątku.

Tak samo wysyłanie wiadomości w Erlangu. Wysłanie wiadomości nie blokuje nikogo, co najwyżej może spowodować zmianę kontekstu zaraz po wysłaniu wiadomości.

Akka Streams mają integrację nie tylko z TCP i HTTP(S), ale także z plikami ( https://doc.akka.io/docs/akka[...]eam-io.html#streaming-file-io ) i bazami danych ( http://slick.lightbend.com/ )

W Eliksirze masz Flow, które oferuje wsparcie dla każdego Enumerable (na ten przykład Stream), więc z automatu masz wsparcie dla plików czy DB. TCP i HTTP są z reguły obsługiwane trochę inaczej, ale z racji, że Erlang jest od początku zaprojektowany jako sRTC to ma naprawdę wiele mechanizmów, które wspierają te założenia, gdzie w Javie to programista musi bardzo często dbać o zachowanie założeń.

Pozostało 580 znaków

2018-06-29 18:40
0
Wibowit napisał(a):

Goroutines to takie wątki, ale w Go, ale nie są one oczywiście tożsame z wątkami w systemie operacyjnym

No i rozmywa się nam pojęcie wątku :]
Z samego artykułu który przytoczyłeś wynika iż scheduler w Go opiera się o cooperative multitasking, a nie o preemptive multitasking. [/quote]

I teraz pytanie: jakie to w tym kontekście będzie miało znaczenie?

Go nie wstrzyma działającej gorutyny o ile ta gorutyna nie wejdzie w jakąś metodę, która współpracuje ze schedulerem Go.

Mogłbyś tutaj wyjaśnić, co masz dokładnie na myśli?

Tymczasem wątki mogą być wywłaszczone w dowolnym momencie (preemption). Formą cooperative multitasking jest np: https://en.wikipedia.org/wiki/Fiber_(computer_science),

Które wątki, w jakim systemie, przy jakim cpu schedulerze? Jak określamy się dokładnie to dokładnie.

Lepiej skupić się na konkretnych problemach i ich rozwiązaniach, a nie na nazywaniu abstrakcji, które ciężko skategoryzować.

Właśnie. ;-)

PS Ja wątek rozumuje prosto - zdolność do przeprowadzania różnych obliczeń dokładnie w tym samym momencie przez ten sam program, w takim sposób, że

  • jeden wątek programy jest niezależny od drugiego (np. jeden nie czeka aż drugi coś skończy)
  • ze strony OSa nie jest konieczny żaden cs do obsłużenia kolejnego wątku, jeśli logical core jest akurat "wolny"

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0