Jak działa Chrome V8? A dlaczego JavaScript nie był skompilowany w JIT?

19

Badałem tłumaczy / kompilatorów, potem natknąłem się na kompilację JIT - w szczególności silnik Javascript V8 Google Chrome.

Moje pytania to:

  1. Jak może być szybszy niż standardowa interpretacja?
  2. Dlaczego w pierwszej kolejności nie użyto kompilacji JIT?


Moje obecne zrozumienie

  1. Każdy program JavaScript zaczyna się jako kod źródłowy , a następnie, niezależnie od metody wykonania, jest ostatecznie tłumaczony na kod maszynowy .
    Zarówno kompilacja JIT, jak i interpretacja muszą podążać tą ścieżką, więc w jaki sposób kompilacja JIT może być szybsza (również dlatego, że JIT jest ograniczony czasowo, w przeciwieństwie do kompilacji AOT)?

  2. Wydaje się, że Kompilacja JIT jest stosunkowo starą innowacją , opartą na artykule JIT-Compilation w Wikipedii .

„Pierwszy opublikowany kompilator JIT jest ogólnie przypisywany pracom nad LISP autorstwa McCarthy'ego w 1960 r .”

„Smalltalk (ok. 1983 ) był pionierem nowych aspektów kompilacji JIT. Na przykład tłumaczenie na kod maszynowy zostało wykonane na żądanie, a wynik został zapisany w pamięci podręcznej do późniejszego użycia. Gdy zabraknie pamięci, system usunie część tego kodu i zregeneruje się kiedy będzie znowu potrzebny ”.

Dlaczego więc na początku interpretowano Javascript ?


Jestem bardzo zdezorientowany i przeprowadziłem wiele badań na ten temat, ale nie znalazłem satysfakcjonujących odpowiedzi.

Docenione zostaną tak jasne, zwięzłe odpowiedzi. I jeśli potrzebne jest dodatkowe wyjaśnienie dotyczące tłumaczy ustnych, kompilatorów JIT itp., Jest to również mile widziane.

Anton Paras
źródło
2
# 2 i # 3 są odpowiedzialne, ale „Jak działa silnik Chrome V8?” bez żadnych kwalifikacji jest zdecydowanie zbyt szeroki; jedyną poprawną odpowiedzią jest link do kodu źródłowego V8. Czy chciałeś zapytać o coś bardziej szczegółowego na temat V8? (jeśli nie, najlepiej byłoby usunąć tę część pytania)
Ixrec
Na drugi rzut oka jedyną kwestią, o którą prosiłem # 1, było zrozumienie # 2, więc go usunę. Dzięki za wkład.
Anton Paras
Nie wspomniano o tym w innych odpowiedziach, ale kompilacja JIT jest trudna. Nie jest to proste, ponieważ błędy wynikające z kompilacji JIT powodują awarie segfaulta zamiast błędów - program ulega awarii zamiast rzucać błąd w konsoli. Tak, dla kompetentnego programisty C znającego się na gdb nie stanowi to problemu. Ale prawie wszyscy kompetentni programiści C znający się na gdb otrzymują wynagrodzenie za pracę przy innych projektach. Niektóre inne języki, takie jak Perl i Ruby, nadal nie mają głównych tłumaczy JIT.
slebetman
Jeśli się zastanawiasz. Mówię o tym z perspektywy głównego programisty / opiekuna języka programowania. Przez kilka lat byłem zatrudniony do obsługi języka programowania Ferite. Jedną z listy życzeń, którą mieliśmy, było wdrożenie JIT. To się nigdy nie zdarzyło - zamiast tego przeprowadziliśmy się. PHP ma niedawno kompilator JIT (HVVM) dzięki Facebookowi, który wlał w to wystarczająco dużo pieniędzy, aby to się stało.
slebetman

Odpowiedzi:

43

Krótka odpowiedź jest taka, że ​​JIT ma dłuższy czas inicjalizacji, ale na dłuższą metę jest znacznie szybszy, a JavaScript nie była pierwotnie przeznaczona na dłuższą metę.

W latach 90. typowy JavaScript na stronie internetowej stanowiłby jedną lub dwie funkcje w nagłówku, a garść kodu osadzonego bezpośrednio we onclickwłaściwościach i tym podobnych. Zwykle uruchamia się dokładnie wtedy, gdy użytkownik spodziewa się ogromnego opóźnienia ładowania strony. Pomyśl o niezwykle podstawowej weryfikacji formy lub drobnych narzędziach matematycznych, takich jak kalkulatory odsetek od kredytów hipotecznych.

Tłumaczenie w razie potrzeby było o wiele prostsze i zapewniało idealnie odpowiednią wydajność do codziennych zastosowań. Jeśli chciałeś czegoś o wydajności długoterminowej, używałeś Flasha lub apletu Java.

Mapy Google w 2004 roku były jedną z pierwszych aplikacji typu killer do intensywnego używania JavaScript. Otworzył oczy na możliwości JavaScript, ale również zwrócił uwagę na problemy z wydajnością. Google spędził trochę czasu, próbując zachęcić przeglądarki do poprawy wydajności JavaScript, a następnie zdecydował, że konkurencja będzie najlepszym czynnikiem motywującym, a także zapewni im najlepsze miejsce w tabeli standardów przeglądarki. Chrome i V8 zostały wydane w 2008 roku. Teraz, 11 lat po pojawieniu się Map Google, mamy nowych programistów, którzy nie pamiętają, by JavaScript był uważany za nieodpowiedni do tego rodzaju zadań.

Powiedz, że masz funkcję animateDraggedMap. Interpretacja może zająć 500 ms, a JIT skompilować 700 ms. Jednak po kompilacji JIT uruchomienie może zająć tylko 100 ms. Jeśli jest to lata 90., a wywołujesz funkcję tylko raz, a następnie ponownie ładujesz stronę, JIT wcale nie jest tego wart. Jeśli jest to dzisiaj i dzwonisz animateDraggedMapsetki lub tysiące razy, to dodatkowe 200 ms przy inicjalizacji to nic, i można to zrobić za kulisami, zanim użytkownik nawet spróbuje przeciągnąć mapę.

Karl Bielefeldt
źródło
2

Dzięki zrozumieniu tego, co dzieje się w czasie wykonywania, można dokonać zmian w kodzie lub interpretacji kodu, które pozwalają na jego szybsze wykonanie lub kompilację, niż jest to znane w czasie kompilacji z wyprzedzeniem.

Można o tym powiedzieć trochę - jest to przedmiotem znacznych badań. Moje własne wyjaśnienie, że zacząłem pisać blady w porównaniu z odpowiedzią podaną w Zrozumienie różnic: tradycyjny interpreter, kompilator JIT, interpreter JIT i kompilator AOT


Po prostu JavaScript nie był początkowo kompilowany ani sprawdzany pod kątem JIT, ponieważ nigdy nie miał być czymś tak złożonym lub ważnym.

Pierwotnym zamiarem Java Script było łącze do apletów Java na stronie internetowej. Możliwość kliknięcia jakiegoś przycisku lub wprowadzenia wartości w polu formularza, a następnie pracy w metodzie apletów Java można zobaczyć w Wywoływanie metod apletów z kodu JavaScript . Możliwe było także, poprzez JavaScript, przejście na inny sposób wywoływania kodu JavaScript z apletu .

Pierwotnym celem JavaScript było połączenie apletów i stron html, które je zawierały. Do tak małego zadania nie potrzeba dużej wydajności (jeśli chcesz wydajności, wywołaj metodę apletu, która jest JIT'ed).

Dopiero po tym, jak Netscape zaczął znaczącą pracę z JavaScript jako własnym językiem i promować go do rozwoju (w tym JavaScript po stronie serwera w Netscape Enterprise Server - który, nawiasem mówiąc, kompilował się przed kompilacją czasu), JavaScript stał się poważnym celem . Po wielu latach niezbędne narzędzia były przydatne.

Społeczność
źródło
1
Nie, JavaScript nie jest powiązany z Javą. Aplety Java to kod bajtowy JVM.
Basile Starynkevitch
@BasileStarynkevitch JavaScript został zaprojektowany do pracy z apletami Java na stronach Hamleta - działając jak klej między domem HTML a metodami zawartymi w obiektach Java. Nie jest i nigdy nie miał być Javą.
JavaScript pierwotnie nazywał się ECMAScript (lub coś takiego) i nie miał nic wspólnego z Javą. Jak to się nazywa JavaScript, jest przedmiotem osobnych badań dla zainteresowanych. To spowodowało zamieszanie od zawsze.
szybko_now
1
@quickly_now i nadal jest tc39.github.io/ecma262
caub
Tak. I z jakiegoś dziwnego powodu, kiedy zwróciłem na to uwagę powyżej, zostałem za to poproszony!
szybko_now
1

JIT są szybkie w JavaScript, ponieważ nie można wygenerować szybkiego kodu maszynowego, gdy nie znasz typu swoich zmiennych.

Gdy nie masz informacji o typie, obliczenia są drogie. Na przykład,

x + y

jest dość skomplikowane, jeśli nie wiesz nic na temat xiy. Mogą to być liczby całkowite, podwójne, łańcuchy, a nawet obiekty, w przypadku których obliczenia te mają skutki uboczne. Ponieważ nie mamy pisania statycznego, jest to kosztowne obliczenie.

Dzięki kompilacji just-in-time możemy wykorzystać informacje o środowisku wykonawczym i przekształcić je w szybsze obliczenia. W czasie wykonywania wersja V8 śledzi rodzaj zmiennych. Jeśli powyższy kod jest wykonywany kilka razy, powiedzmy, ciągami znaków, kompilator może wykonać znacznie prostsze instrukcje dotyczące łączenia ciągów znaków. Kiedy kompilator osiągnie x + y, zamiast uruchamiać dużo kodu rozgałęziającego się dla wielu różnych typów xiy, kompilator szybko sprawdza, czy mamy ponownie łańcuchy, a następnie wykonuje tylko kilka wierszy kodu maszynowego, który konkretnie łączy łańcuchy.

Na przykład w C ++ kompilator zna wcześniej typy xiy, ponieważ musieliśmy zadeklarować zmienne. Dzięki temu może wygenerować zoptymalizowany kod maszynowy do łączenia łańcuchów przed uruchomieniem kodu.

użytkownik835611
źródło
0

1) Jak może być szybszy niż standardowa interpretacja? Cóż, przemyślany przykład byłby następujący; załóżmy, że mamy 2 aplikacje ApplicationCompiled i ApplicationInterpreted. Oba te programy robią dokładnie to samo i współużytkują ten sam kod źródłowy. Kompilacja aplikacji zajmuje 6 sekund.

Powiedzmy, że czasy scenariusza A są następujące:

  • Dla ApplicationCompiled: 4 sekundy
  • W przypadku interpretacji aplikacji: 12 sekund

Tak więc w sumie ApplicationCompiled zajmuje 10 sekund, aby uruchomić Scenariusz A (kompilacja 6 sekund, 4 sekundy), a ApplicationInterpreted zajmuje w sumie 12 sekund. Nie mam konkretnego przykładu do pokazania i nie jestem pewien, w jakich przypadkach powyższe byłoby prawdą - zależy to również w dużej mierze od stopnia inteligencji interpretacji i kompilatora.

Oczywiście jest to bardzo uproszczone, ale ten sam pomysł można zastosować do kompilacji / interpretacji JIT. Następne pytanie brzmiałoby: „jak ustalić - przy niskich kosztach - czy gałąź ta powinna zostać skompilowana lub zinterpretowana JIT”? Jestem poza moją ligą tutaj :)

2) Dlaczego nie użyto kompilacji JIT? Nie wiem, ale uważam, że jest to po prostu kwestia zasobów i dojrzałości dostępnych postępów w tworzeniu trudnego do optymalizacji języka, takiego jak JavaScript, stosującego zaawansowane techniki takie jak te. Prawdopodobnie było wtedy dużo wiszących owoców.

cwap
źródło