Jak jest Node.js z natury szybszy, gdy nadal opiera się na wątkach wewnętrznych?

Właśnie obejrzałem następujący film: Wprowadzenie do Node.js i nadal nie rozumiem, jak można uzyskać korzyści z prędkości.

Głównie w pewnym momencie Ryan Dahl (Node.js ' creator) mówi, że węzeł.js jest oparty na pętli zdarzeń, a nie na wątku. Wątki są drogie i powinny być pozostawione tylko ekspertom od programowania współbieżnego do wykorzystania.

Później pokazuje stos architektury węzła.js, który ma podstawową implementację C, która posiada własną pulę wątków wewnętrznie. Więc oczywiście Node.Programiści js nigdy nie uruchamialiby własnych wątków ani nie używali puli wątków bezpośrednio...używają asynchronicznych połączeń zwrotnych. Tyle Rozumiem.

To czego nie rozumiem to punkt ten węzeł.js nadal używa threads...it to tylko ukrywanie implementacji więc jak to jest szybsze skoro 50 osób prosi o 50 plików (obecnie nie w pamięci) to nie jest wymagane 50 wątków?

Jedyną różnicą jest to, że skoro jest zarządzany wewnętrznie węzeł.programista js nie musi kodować wątków, ale pod spodem nadal używa wątków do przetwarzania żądań plików IO (blokowania).

Więc naprawdę nie bierzesz tylko jednego problemu (threading) i ukrywasz go, dopóki ten problem nadal istnieje: głównie wiele wątków, przełączanie kontekstu, martwe blokady...itd?

Musi być jakiś szczegół, którego nadal nie rozumiem.

Author: Ry-, 2010-09-02

6 answers

W rzeczywistości jest kilka różnych rzeczy, które są ze sobą powiązane. Ale zaczyna się od memu, że wątki są po prostu naprawdę trudne. Więc jeśli są trudne, jesteś bardziej prawdopodobne, gdy używasz wątków do 1) złamać z powodu błędów i 2) nie używać ich tak efektywnie, jak to możliwe. (2) jest tym, o który pytasz.

Pomyśl o jednym z podanych przez niego przykładów, gdzie pojawia się żądanie i uruchamiasz jakieś zapytanie, a następnie robisz coś z wynikami tego. Jeśli napiszesz to w standardowej procedurze sposób, kod może wyglądać tak:

result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );

Jeśli pojawiające się żądanie spowodowało utworzenie nowego wątku, który uruchomił powyższy kod, będziesz miał tam wątek, który w ogóle nie robi nic, podczas gdy query() jest uruchomiony. (Apache, według Ryana, używa jednego wątku, aby spełnić pierwotne żądanie, podczas gdy nginx przewyższa go w przypadkach, o których mówi, ponieważ nie jest.)

Gdybyś był naprawdę sprytny, wyraziłbyś powyższy kod w taki sposób, że środowisko może wyłączyć się i zrobić coś innego podczas uruchamiania zapytania:
query( statement: "select smurfs from some_mushroom", callback: go_do_something_with_result() );

To jest w zasadzie to, co węzeł.js robi. Zasadniczo dekorujesz-w sposób, który jest wygodny ze względu na język i środowisko, stąd punkty o zamknięciach-Twój kod w taki sposób, że środowisko może być sprytne o tym, co działa i kiedy. W ten sposób node.js nie jest nowym w tym sensie, że wymyślił asynchroniczne We/Wy (nie żeby ktoś twierdził coś takiego), ale to jest nowe, ponieważ sposób wyrażania jest trochę inny.

Uwaga: Kiedy mówię, że środowisko może być mądre co do tego, co działa i kiedy, konkretnie chodzi mi o to, że wątek użyty do uruchomienia niektórych we / wy może być teraz używany do obsługi innych żądań lub obliczeń, które można wykonać równolegle lub uruchomić inne równoległe We/Wy. (nie jestem pewien, że węzeł jest wystarczająco wyrafinowany, aby rozpocząć więcej pracy dla tego samego żądania, ale masz pomysł.)

 142
Author: jrtipton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 20:31:15

Uwaga!To stara odpowiedź. Chociaż nadal jest to prawdą w ogólnym zarysie, niektóre szczegóły mogły ulec zmianie z powodu szybkiego rozwoju Node w ciągu ostatnich kilku lat.

Używa wątków ponieważ:

  1. O_NONBLOCK opcja open() nie działa na plikach.
  2. istnieją biblioteki innych firm, które nie oferują nieblokujących IO.

Aby sfałszować nieblokujące IO, wątki są konieczne: blokowanie IO w osobnym wątku. Jest to brzydkie rozwiązanie i powoduje wiele kosztów.

Jest jeszcze gorzej na poziomie sprzętowym:

  • z DMA procesor asynchronicznie odciąża IO.
  • Dane są przesyłane bezpośrednio między urządzeniem IO a pamięcią.
  • jądro zawija to w synchroniczne, blokujące wywołanie systemowe.
  • węzeł.js zawija blokujące wywołanie systemowe w wątek.

To jest po prostu głupie i nieefektywne. Ale przynajmniej działa! Możemy cieszyć się węzłem.js bo to ukrywa brzydkie i uciążliwe szczegóły za architekturą asynchroniczną sterowaną zdarzeniami.

Może ktoś w przyszłości zaimplementuje O_NONBLOCK dla plików?...

Edit: rozmawiałem o tym z przyjacielem i powiedział mi, że alternatywą dla wątków jest przepytywanie za pomocą select : podaj timeout 0 i zrób IO na zwracanych deskryptorach plików (teraz, gdy mają gwarancję, że nie zablokują).

 32
Author: nalply,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-13 08:35:40

Obawiam się, że" robię źle " tutaj, jeśli tak, Usuń mnie i przepraszam. W szczególności nie widzę, jak tworzę schludne małe adnotacje, które niektórzy ludzie stworzyli. Mam jednak wiele obaw/spostrzeżeń do poczynienia w tym wątku.

1) komentowany element w pseudo-kodzie w jednej z popularnych odpowiedzi

result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );

Jest zasadniczo fałszywy. Jeśli wątek jest komputerowy, to nie kręci kciukami, tylko wykonuje niezbędną pracę. Jeśli z drugiej strony jest to po prostu czekając na zakończenie IO, to jest nie używając czasu procesora, cały punkt infrastruktury kontroli wątków w jądrze jest taki, że procesor znajdzie coś przydatnego do zrobienia. Jedynym sposobem na "przekręcenie kciuków", jak sugerowano tutaj, byłoby stworzenie pętli ankietowej, a nikt, kto zakodował prawdziwy serwer internetowy, nie jest wystarczająco nieudolny, aby to zrobić.

2) "wątki są trudne", ma sens tylko w kontekście udostępniania danych. Jeśli masz zasadniczo niezależne wątki, takie jak podczas obsługi niezależnych żądań internetowych, threading jest trywialnie prosty, wystarczy zakodować liniowy przepływ obsługi jednego zadania i siedzieć spokojnie wiedząc, że obsłuży wiele żądań, a każde będzie skutecznie niezależne. Osobiście zaryzykowałbym, że dla większości programistów uczenie się mechanizmu zamknięcia/wywołania zwrotnego jest bardziej złożone niż zwykłe kodowanie wersji wątku od góry do dołu. (Ale tak, jeśli musisz komunikować się między wątkami, życie staje się naprawdę trudne, naprawdę szybko, ale wtedy jestem nieprzekonany, że mechanizm zamknięcia / callback naprawdę to zmienia, to po prostu ogranicza swoje opcje, ponieważ takie podejście jest nadal osiągalne z wątków. W każdym razie, to zupełnie inna dyskusja, która naprawdę nie ma tu znaczenia).

3) do tej pory nikt nie przedstawił żadnych rzeczywistych dowodów na to, dlaczego jeden konkretny rodzaj przełącznika kontekstowego byłby bardziej lub mniej czasochłonny niż jakikolwiek inny typ. Moje doświadczenie w tworzeniu wielozadaniowych kerneli (na małą skalę dla embedded Kontrolery, nic tak fantazyjnego jak "prawdziwy" OS) sugeruje, że tak nie byłoby.

4) Wszystkie ilustracje, które widziałem do tej pory, które mają pokazać, jak dużo szybszy jest Node niż inne serwery są strasznie wadliwe, jednak są wadliwe w sposób, który pośrednio ilustruje jedną przewagę, którą zdecydowanie zaakceptowałbym dla Node (i nie jest to bynajmniej nieistotne). Node nie wygląda na to, że wymaga (ani nawet nie pozwala) strojenia. Jeśli masz model gwintowany, potrzebujesz aby utworzyć wystarczającą ilość wątków do obsługi oczekiwanego obciążenia. Zrób to źle, a skończysz z słabą wydajnością. Jeśli jest zbyt mało wątków, to procesor jest bezczynny, ale nie jest w stanie zaakceptować większej liczby żądań, utworzyć zbyt wielu wątków i marnujesz pamięć jądra, a w przypadku środowiska Java marnujesz również pamięć sterty głównej. Teraz, dla Javy, marnowanie sterty jest pierwszym, najlepszym, sposobem, aby schrzanić wydajność systemu, ponieważ efektywne garbage collection (obecnie może to zmienić z G1, ale wydaje się, że jury jest nadal w tym punkcie od początku 2013 co najmniej) zależy od posiadania dużo zapasowej sterty. Więc jest problem, dostroić go za mało wątków, masz bezczynne procesory i słabą przepustowość, dostroić go za dużo, i to w inny sposób.

5) jest inny sposób, w którym akceptuję logikę twierdzenia, że podejście Node ' a "jest szybsze z założenia", i to jest to. Większość modeli wątków używa pokrojonego w czasie modelu przełącznika kontekstowego, warstwowego bardziej odpowiedni (ocena wartości :) i bardziej skuteczny (Nie ocena wartości) model prewencyjny. Dzieje się tak z dwóch powodów, po pierwsze, większość programistów zdaje się nie rozumieć preemption priority, a po drugie, jeśli nauczysz się threadingu w środowisku windows, timeslicing jest tam, czy ci się to podoba, czy nie (oczywiście wzmacnia to pierwszy punkt; w szczególności, pierwsze wersje Javy używały preemption priority w implementacjach Solarisa, a timeslicing w Windows. Ponieważ większość programiści nie rozumieli i narzekali, że "threading nie działa w Solarisie" zmienili model na czasowy). W każdym razie, najważniejsze jest to, że timeslicing tworzy dodatkowe (i potencjalnie niepotrzebne) przełączniki kontekstowe. Każdy przełącznik kontekstowy zajmuje czas procesora, a czas ten jest skutecznie usuwany z pracy, którą można wykonać w rzeczywistym miejscu pracy. Jednak ilość czasu zainwestowanego w zmianę kontekstu z powodu timeslicingu nie powinna być większa niż bardzo mała procent całego czasu, chyba, że dzieje się coś dość dziwacznego i nie widzę powodu, aby oczekiwać, że tak będzie w przypadku prostego serwera internetowego). Tak więc, tak, nadmiarowe przełączniki kontekstowe związane z timeslicingiem są nieefektywne (i nie zdarzają się one z reguły w wątkach jądra, btw), ale różnica będzie wynosić kilka procent przepustowości, a nie rodzaj czynników liczby całkowitej, które są implikowane w twierdzeniach dotyczących wydajności, które są często implikowane dla Węzeł.

W każdym razie przepraszam, że to wszystko jest długie i chaotyczne, ale naprawdę czuję, że do tej pory dyskusja niczego nie udowodniła i byłbym zadowolony, słysząc od kogoś w żadnej z tych sytuacji:

A) prawdziwe wyjaśnienie, dlaczego Node powinien być lepszy (poza dwoma scenariuszami, które opisałem powyżej, z których pierwszy (słabe strojenie), jak sądzę, jest prawdziwym wyjaśnieniem wszystkich testów, które widziałem do tej pory. [edytuj] właściwie im więcej o tym myślę, tym bardziej się zastanawiam jeśli pamięć używana przez ogromną liczbę stosów może być tutaj znacząca. Domyślne rozmiary stosów dla nowoczesnych wątków są zazwyczaj dość duże, ale pamięć przydzielona przez system zdarzeń oparty na zamknięciu byłaby tylko tym, co jest potrzebne)

B) prawdziwy benchmark, który daje szansę wybranemu serwerowi z wątkami. Przynajmniej w ten sposób musiałbym przestać wierzyć, że twierdzenia są zasadniczo fałszywe ; > ([edytuj] to chyba raczej silniejsze niż zamierzałem, ale czuję, że wyjaśnienia dotyczące korzyści z wykonania są w najlepszym razie niekompletne, a przedstawione wartości odniesienia są nieuzasadnione).

Pozdrawiam, Toby
 29
Author: Toby Eggitt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-28 02:19:59

To czego nie rozumiem to punkt ten węzeł.js nadal używa wątków.

Ryan używa wątków dla tych części, które blokują (Większość node.js używa non-blocking IO), ponieważ niektóre części są szalenie trudne do napisania non blocking. Ale wierzę, że Ryan chce mieć wszystko bez blokowania. Na slide 63(internal design) widać, że Ryan używa libev (biblioteki, która abstrahuje asynchroniczne Powiadamianie o zdarzeniach) dla nieblokującego eventloop . Na węzeł pętli zdarzenia.js wymaga mniejszych wątków, co zmniejsza przełączanie kontekstu, zużycie pamięci itp.

 14
Author: Alfred,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 20:06:46

Wątki są używane tylko do obsługi funkcji nie mających funkcji asynchronicznych, takich jak stat().

Funkcja stat() jest zawsze blokująca, więc node.js musi użyć wątku, aby wykonać rzeczywiste wywołanie bez blokowania głównego wątku (pętla zdarzeń). Potencjalnie żaden wątek z puli wątków nigdy nie będzie używany, jeśli nie trzeba wywoływać tego rodzaju funkcji.

 11
Author: gawi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-19 01:10:58

Nie wiem nic o wewnętrznym funkcjonowaniu node.js, ale widzę, jak używanie pętli zdarzeń może przewyższyć obsługę We/Wy. Wyobraź sobie żądanie płyty, daj mi staticFile.x, zrób 100 żądań tego pliku. Każde żądanie zwykle zajmuje wątek odzyskujący ten plik, czyli 100 wątków.

Teraz wyobraź sobie, że pierwsze żądanie tworzy jeden wątek, który staje się obiektem publisher, wszystkie 99 innych żądań najpierw sprawdza, czy istnieje obiekt publisher dla staticFile.x, jeśli tak, to posłuchaj podczas wykonywania swojej pracy, w przeciwnym razie uruchom nowy wątek, a tym samym nowy obiekt wydawcy.

Po wykonaniu pojedynczego wątku przechodzi on przez plik staticFile.x do wszystkich 100 słuchaczy i niszczy się, więc następne żądanie tworzy nowy wątek i obiekt wydawcy.

Więc jest to 100 wątków vs 1 wątek w powyższym przykładzie, ale także 1 wyszukiwanie dysku zamiast 100 szukania dysku, wzmocnienie może być dość fenomenalne. Ryan to mądry facet!

Innym sposobem spojrzenia na is jest jeden z jego przykładów na początku filmu. Zamiast:

pseudo code:
result = query('select * from ...');

Ponownie, 100 osobnych zapytań do bazy danych versus...:

pseudo code:
query('select * from ...', function(result){
    // do stuff with result
});

Jeśli zapytanie już było wysyłane, inne równe zapytania po prostu przeskakiwałyby na pasmowy, więc możesz mieć 100 zapytań w jednej rundzie bazy danych.

 7
Author: BGerrissen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 19:45:15