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:
- Jak może być szybszy niż standardowa interpretacja?
- Dlaczego w pierwszej kolejności nie użyto kompilacji JIT?
Moje obecne zrozumienie
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)?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.
źródło
Odpowiedzi:
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
onclick
wł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 dzwoniszanimateDraggedMap
setki 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ę.źródło
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.
źródło
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.
źródło
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:
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.
źródło