Czy przeglądarki parsują javascript przy każdym ładowaniu strony?

190

Czy przeglądarki (IE i Firefox) analizują połączone pliki javascript za każdym razem, gdy strona jest odświeżana?

Mogą buforować pliki, więc domyślam się, że nie będą próbować ich pobierać za każdym razem, ale ponieważ każda strona jest zasadniczo osobna, spodziewam się, że usuną stary kod i go parsują.

Jest to nieefektywne, choć doskonale zrozumiałe, ale zastanawiam się, czy współczesne przeglądarki są wystarczająco sprytne, aby uniknąć kroku analizy w witrynach. Mam na myśli przypadki, w których witryna korzysta z biblioteki javascript, takiej jak ExtJS lub jQuery itp.

ajreal
źródło
4
Mój 2c: Wydaje mi się, że korzyści płynące z buforowania przeanalizowanych plików JavaScript są zbyt małe, aby mogło to być znaczącą optymalizacją.
Itay Maman,
2
Z moich testów może to mieć znaczenie. Na przykład czas ładowania jQuery wynosi około 30 ms (na szybkiej maszynie stacjonarnej), z czego 20% parsuje tylko kod w reprezentacji wykonywalnej, a reszta wykonuje go, tj. W tym przypadku inicjuje obiekt jQuery. Jeśli korzystasz z telefonu komórkowego i korzystasz z dwóch lub trzech bibliotek, to opóźnienie może być istotne, ponieważ wykonywanie JavaScript jest blokowane, a strona jest zasadniczo pusta do momentu załadowania każdego skryptu JS do pamięci.
djjeck

Odpowiedzi:

338

To są szczegóły, które udało mi się wykopać. Warto przede wszystkim zauważyć, że chociaż JavaScript jest zwykle uważany za interpretowany i uruchamiany na maszynie wirtualnej, tak naprawdę nie jest tak w przypadku współczesnych interpreterów, które mają tendencję do kompilowania źródła bezpośrednio w kodzie maszynowym (z wyjątkiem IE).


Chrome: silnik V8

V8 ma pamięć podręczną kompilacji. Przechowuje skompilowany JavaScript przy użyciu skrótu źródła dla maksymalnie 5 kolekcji śmieci. Oznacza to, że dwa identyczne fragmenty kodu źródłowego będą współdzielić wpis pamięci podręcznej w pamięci, niezależnie od tego, w jaki sposób zostały uwzględnione. Ta pamięć podręczna nie jest czyszczona po ponownym załadowaniu stron.

Źródło


Aktualizacja - 19.03.2015

Zespół Chrome opublikował szczegółowe informacje na temat swoich nowych technik przesyłania strumieniowego JavaScript i buforowania .

  1. Streaming skryptu

Strumieniowe przesyłanie skryptów optymalizuje parsowanie plików JavaScript. [...]

Począwszy od wersji 41, Chrome analizuje skrypty asynchroniczne i odroczone w osobnym wątku, gdy tylko pobieranie się rozpocznie. Oznacza to, że parsowanie może zakończyć się zaledwie milisekundy po zakończeniu pobierania, co powoduje, że strony ładują się nawet o 10% szybciej.

  1. Buforowanie kodu

Zwykle silnik V8 kompiluje kod JavaScript strony przy każdej wizycie, zamieniając go w instrukcje zrozumiałe dla procesora. Ten skompilowany kod jest następnie odrzucany, gdy użytkownik opuści stronę, ponieważ skompilowany kod jest wysoce zależny od stanu i kontekstu maszyny w czasie kompilacji.

Chrome 42 wprowadza zaawansowaną technikę przechowywania lokalnej kopii skompilowanego kodu, dzięki czemu po powrocie na stronę można pominąć pobieranie, analizowanie i kompilowanie. Przy wszystkich ładowaniach stron pozwala to Chrome uniknąć około 40% czasu kompilacji i oszczędza cenną baterię na urządzeniach mobilnych.


Opera: Carakan Engine

W praktyce oznacza to, że za każdym razem, gdy program skryptowy ma zostać skompilowany, którego kod źródłowy jest identyczny z kodem innego programu, który został niedawno skompilowany, ponownie wykorzystujemy poprzednie wyjście kompilatora i całkowicie pomijamy krok kompilacji. Ta pamięć podręczna jest dość skuteczna w typowych scenariuszach przeglądania, w których jedna strona ładuje się strona po stronie z tej samej witryny, takich jak różne artykuły z serwisu informacyjnego, ponieważ każda strona często ładuje tę samą, czasem bardzo dużą bibliotekę skryptów.

Dlatego JavaScript jest buforowany podczas przeładowywania stron, dwa żądania do tego samego skryptu nie spowodują ponownej kompilacji.

Źródło


Firefox: silnik SpiderMonkey

SpiderMonkey używa Nanojitjako natywnego zaplecza kompilatora JIT. Proces kompilacji kodu maszynowego można zobaczyć tutaj . Krótko mówiąc, wydaje się , że rekompilują skrypty podczas ich ładowania. Jeśli jednak przyjrzymy się bliżej elementom wewnętrznym Nanojit, zobaczymy, że monitor wyższego poziomu jstracer, który służy do śledzenia kompilacji, może przechodzić przez trzy etapy podczas kompilacji, zapewniając korzyści Nanojit:

Początkowy stan monitora śledzenia to monitorowanie. Oznacza to, że spidermonkey interpretuje kod bajtowy. Za każdym razem, gdy spidermonkey interpretuje kod bajtowy skoku do tyłu, monitor odnotowuje liczbę przeskoków do wartości licznika programu skoku docelowego (PC). Ten numer nazywa się liczbą trafień na PC. Jeśli liczba trafień konkretnego komputera osiągnie wartość progową, cel zostanie uznany za gorący.

Gdy monitor zdecyduje, że komputer docelowy jest gorący, szuka w haszcie fragmentów, aby sprawdzić, czy istnieje fragment zawierający natywny kod dla tego komputera docelowego. Jeśli znajdzie taki fragment, przechodzi do trybu wykonywania. W przeciwnym razie przejdzie w tryb nagrywania.

Oznacza to, że dla hotfragmentów kodu kod macierzysty jest buforowany. Oznacza to, że nie trzeba go ponownie kompilować. Nie jest wyjaśnione, czy te zaszyfrowane sekcje natywne są zachowywane między odświeżeniami strony. Ale przypuszczam, że są. Jeśli ktoś może znaleźć na to dowody potwierdzające, to doskonale.

EDYCJA : Wskazano, że twórca Mozilli Boris Zbarsky stwierdził, że Gecko nie buforuje jeszcze skompilowanych skryptów . Zaczerpnięte z tej SO odpowiedzi .


Safari: JavaScriptCore / SquirelFish Engine

Myślę, że najlepsza odpowiedź na to wdrożenie została już udzielona przez kogoś innego .

Obecnie nie buforujemy kodu bajtowego (ani kodu natywnego). Jest to
opcja, którą rozważaliśmy, jednak obecnie generowanie kodu jest
trywialną częścią czasu wykonywania JS (<2%), więc obecnie nie zajmujemy się
tym.

Napisał to Maciej Stachowiak , główny programista Safari. Myślę więc, że możemy uznać to za prawdę.

Nie udało mi się znaleźć żadnych innych informacji, ale możesz przeczytać więcej na temat poprawy prędkości najnowszego SquirrelFish Extremesilnika tutaj lub przejrzeć kod źródłowy , jeśli czujesz się odważny.


IE: Silnik czakry

Nie ma aktualnych informacji na temat silnika JavaScript IE9 (czakry) w tym polu. Jeśli ktoś coś wie, prosimy o komentarz.

Jest to dość nieoficjalne, ale w przypadku starszych implementacji silnika IE Eric Lippert ( programista MS JScript ) stwierdza w odpowiedzi na blogu , że:

JScript Classic działa jak język skompilowany w tym sensie, że przed uruchomieniem dowolnego programu JScript Classic w pełni sprawdzamy składnię kodu, generujemy pełne drzewo parsowania i generujemy kod bajtowy. Następnie uruchamiamy kod bajtowy przez interpreter kodu bajtowego. W tym sensie JScript jest tak samo „skompilowany” jak Java. Różnica polega na tym, że JScript nie pozwala na utrwalanie ani sprawdzanie naszego zastrzeżonego kodu bajtowego . Ponadto kod bajtowy jest znacznie wyższy niż kod bajtowy JVM - język kodu bajtowego JScript Classic to niewiele więcej niż linearyzacja drzewa parsowania, podczas gdy kod bajtowy JVM jest wyraźnie przeznaczony do działania na maszynie niskiego poziomu.

Sugeruje to, że kod bajtowy nie utrzymuje się w żaden sposób, a zatem kod bajtowy nie jest buforowany.

Jivings
źródło
10
+1, doskonałe napisanie. Jeśli chodzi o Firefoksa, zapoznaj się z pytaniem StackOverflow, w którym twórca Mozilli Boris Zbarsky wyjaśnia, że ​​Gecko obecnie tego nie robi.
cha0site
Dzięki, widziałem to podczas moich podróży, ale nie mogłem znaleźć żadnych innych dowodów potwierdzających. Przeredaguję odpowiedź.
Jivings
1
Zwróć uwagę, że to, co zostało powiedziane o IE, zostało powiedziane w 2003 roku: Pierwsze wydanie silnika JS IE9 miało miejsce w IE9 w 2011 roku.
gsnedders
Ponadto Opera buforuje kod bajtowy JS na więcej niż tylko przeładowaniach. (Wygenerowany kod maszynowy nie jest jednak buforowany).
gsnedders
2
@Jivings Weź powyższe jako źródło. (Jestem jedną z osób w zespole Carakan.)
gsnedders
12

Opera to robi, jak wspomniano w drugiej odpowiedzi. ( źródło )

Firefox (silnik SpiderMonkey) nie buforuje kodu bajtowego. ( źródło )

WebKit (Safari, Konqueror) nie buforuje kodu bajtowego. ( źródło )

Nie jestem pewien co do IE [6/7/8] lub V8 (Chrome), myślę, że IE może wykonać jakieś buforowanie, podczas gdy V8 może nie. IE jest zamkniętym źródłem, więc nie jestem pewien, ale w V8 buforowanie „skompilowanego” kodu może nie mieć sensu, ponieważ kompilują się bezpośrednio do kodu maszynowego.

cha0site
źródło
1
IE6–8 prawie na pewno nie. IE9 może, ale i tak nie mam żadnych dowodów. Skompilowanego JS prawdopodobnie nie ma nigdzie w pamięci podręcznej, ponieważ często jest dość duży.
gsnedders
@gsnedders: Nie jestem pewien, czy IE8 nie jest w stanie tego zrobić technicznie, wydaje się, że zbyt się kompiluje, by kod bajtowy (nie oficjalny, ale zamknięty), więc nie ma technicznego powodu, aby tego nie buforować. Wydaje się, że IE9 dodaje JIT do kompilacji do kodu natywnego.
cha0site
2
Kod bajtowy był używany przez IE od… na zawsze. To nic nowego w IE8. Chodzi tylko o to, że biorąc pod uwagę tłumacza, jego działanie jest znacznie wolniejsze niż czas parsowania, co jest zupełnie nieistotne. IE9 ma zupełnie nowy (od zera) silnik JS, więc nic między nimi nie następuje.
gsnedders
3

O ile mi wiadomo, tylko Opera buforuje przeanalizowany JavaScript. Zobacz sekcję „Skompilowane programy w pamięci podręcznej” tutaj .

gsnedders
źródło
dzięki, czy masz więcej szczegółów na temat innej rodziny przeglądarek?
ajreal
2

Nie ma znaczenia, że Google Dart wyraźnie rozwiązuje ten problem za pomocą „migawek” - celem jest przyspieszenie inicjalizacji i czasu ładowania poprzez załadowanie przygotowanej wersji kodu.

InfoQ ma dobry opis @ http://www.infoq.com/articles/google-dart

igrigorik
źródło
0

Myślę, że poprawna odpowiedź brzmiałaby „nie zawsze”. Z tego, co rozumiem, zarówno przeglądarka, jak i serwer odgrywają rolę w określaniu, co zostanie buforowane. Jeśli naprawdę potrzebujesz przeładowywać pliki za każdym razem, myślę, że powinieneś być w stanie skonfigurować to z poziomu Apache (na przykład). Oczywiście przypuszczam, że przeglądarkę użytkownika można skonfigurować tak, aby ignorowała to ustawienie, ale jest to prawdopodobnie mało prawdopodobne.

Więc wyobrażam sobie, że w większości praktycznych przypadków same pliki javascript są buforowane, ale są dynamicznie ponownie interpretowane przy każdym ładowaniu strony.

Zachary Murray
źródło
0

Przeglądarka zdecydowanie korzysta z buforowania, ale tak, przeglądarki analizują JavaScript za każdym razem, gdy strona jest odświeżana. Ponieważ za każdym razem, gdy strona jest ładowana przez przeglądarkę, tworzy 2 drzewa 1. Drzewo zawartości i 2. drzewo wyrenderowania.

To drzewo renderowania składa się z informacji o układzie wizualnym elementów dom. Tak więc za każdym razem, gdy strona się ładuje, javascript jest analizowany, a wszelkie dynamiczne zmiany w javascript będą podobały się pozycjonowaniu elementu dom, show / hide element, add / remove element spowoduje, że przeglądarka odtworzy drzewo renderowania. Ale współczesne przeglądarki, takie jak FF i chrome, traktują to nieco inaczej, mają koncepcję przyrostowego renderowania, więc za każdym razem, gdy pojawiają się dynamiczne zmiany w js, jak wspomniano powyżej, spowoduje to tylko renderowanie i ponowne malowanie tych elementów.

Abhidev
źródło