Dlaczego new slow?
Benchmark:
Niezmienniki:
var f = function() { };
var g = function() { return this; }
Testy:
Poniżej w kolejności oczekiwanej prędkości
new f;
g.call(Object.create(Object.prototype));
new (function() { })
(function() { return this; }).call(Object.create(Object.prototype));
Rzeczywista prędkość:
new f;
g.call(Object.create(Object.prototype));
(function() { return this; }).call(Object.create(Object.prototype));
new (function() { })
Pytanie:
- kiedy zamienisz
f
ig
na funkcje anonimowe inline. Dlaczegonew
(test 4.) testować wolniej?
Update:
Co konkretnie powoduje, że new
są wolniejsze, gdy f
i g
są inlined.
Interesują mnie odniesienia do specyfikacji ES5 lub odniesienia do JagerMonkey lub kodu źródłowego V8. (Możesz też połączyć kod źródłowy JSC i Carakan. Oh I Zespół IE może wyciekać źródło Czakry, jeśli chcą).
Jeśli podlinkujesz jakieś źródło silnika JS, wyjaśnij to.
5 answers
Problem polega na tym, że możesz sprawdzić aktualny kod źródłowy różnych silników, ale to Ci nie pomoże. Nie próbuj przechytrzyć kompilatora. I tak spróbują zoptymalizować pod kątem najczęstszego użycia. Nie wydaje mi się, aby (function() { return this; }).call(Object.create(Object.prototype))
1000 razy miał prawdziwy przypadek użycia.
"programy powinny być pisane dla ludzi do czytania, a nawiasem mówiąc tylko dla maszyn do wykonywania."
Abelson & Sussman, SICP, Przedmowa do pierwszego edition
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
2011-06-24 21:02:19
Główna różnica między #4 A wszystkimi innymi przypadkami polega na tym, że pierwszy raz, gdy używasz zamknięcia jako konstruktora, jest zawsze dość drogi.
Jest on zawsze obsługiwany w środowisku uruchomieniowym V8 (Nie w generowanym kodzie), a przejście między skompilowanym kodem JS a środowiskiem uruchomieniowym C++ jest dość kosztowne. Kolejne alokacje są zwykle obsługiwane w wygenerowanym kodzie. Możesz spojrzeć na
Generate_JSConstructStubHelper
wbuiltins-ia32.cc
i zauważyć, że jest toRuntime_NewObject
, gdy zamknięcie nie ma początkowej mapy. (patrz http://code.google.com/p/v8/source/browse/trunk/src/ia32/builtins-ia32.cc#138)Kiedy closure jest używany jako konstruktor po raz pierwszy, V8 musi utworzyć nową mapę (aka hidden class) I przypisać ją jako initial map dla tego zamknięcia. Zobacz http://code.google.com/p/v8/source/browse/trunk/src/heap.cc#3266 . Ważne jest to, że mapy są przydzielane w oddzielnej przestrzeni pamięci. Ta przestrzeń nie może być czyszczona przez szybkie częściowe scavenge kolekcjoner. W przypadku przepełnienia przestrzeni mapy V8 musi wykonać stosunkowo drogie pełne Mark-sweep GC.
Jest kilka innych rzeczy, które dzieją się, gdy używasz closure jako konstruktora po raz pierwszy, ale 1 i 2 są głównymi przyczynami powolności przypadku testowego #4.
Jeśli porównamy wyrażenia #1 i #4 to różnice są następujące:
- # 1 nie przydziela za każdym razem nowego zamknięcia;
- # 1 nie wchodzi za każdym razem w runtime: po closure gets initial map construction jest obsługiwany w szybkiej ścieżce generowanego kodu. Obsługa całej konstrukcji w wygenerowanym kodzie jest znacznie szybsza niż przechodzenie tam iz powrotem między uruchomieniem a wygenerowanym kodem;
- # 1 nie przydziela nowej mapy początkowej dla każdego nowego zamknięcia za każdym razem;
- # 1 nie powoduje zamiatania znaczników przez przepełnioną przestrzeń Mapy (tylko tanie zmioty).
Jeśli porównamy #3 i # 4 to różnice są następujące:
- # 3 nie przydziela nowego pierwsza mapa dla każdego nowego zamknięcia za każdym razem;
- #3 nie powoduje zamiatania znaczników przez przepełnioną przestrzeń Mapy (tylko tanie zamiatarki);
- #4 robi mniej po stronie JS (brak funkcji.prototyp.telefon, bez obiektu.create, no Object.prototype lookup etc) więcej Po Stronie C++ (#3 wchodzi również w runtime za każdym razem, gdy robisz obiekt.tworzyć, ale robi tam bardzo mało).
Najważniejsze jest to, że pierwsze użycie closure jako konstruktora jest kosztowne w porównaniu z kolejnymi wywołaniami konstrukcyjnymi z tego samego zamknięcia, ponieważ V8 musi skonfigurować jakąś instalację wodno-kanalizacyjną. Jeśli natychmiast odrzucimy zamknięcie, w zasadzie wyrzucamy całą pracę wykonaną przez V8, aby przyspieszyć kolejne wywołania konstruktora.
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
2018-10-01 09:40:21
Domyślam się, że następujące rozszerzenia wyjaśniają, co się dzieje w V8:
- t (exp1): t (Tworzenie obiektu)
- t (exp2) : t (Tworzenie obiektu przez obiekt.create ())
- t (exp3) : t (Tworzenie obiektu przez obiekt.create ()) + t (Function object Creation)
-
T (exp4): t ( Tworzenie obiektu) + T(Tworzenie obiektu funkcji) + t (Tworzenie obiektu klasy) [w Chrome]
- dla ukrytych klas w Chrome spójrz na : http://code.google.com/apis/v8/design.html .
- gdy nowy obiekt jest tworzony przez obiekt.Utwórz nie musi być tworzony nowy obiekt klasy. Istnieje już taka, która jest używana dla literałów obiektowych i nie ma potrzeby tworzenia nowej klasy.
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
2011-06-24 14:23:34
Cóż, te dwa telefony nie robią dokładnie tego samego. Rozważ ten przypadek:
var Thing = function () {
this.hasMass = true;
};
Thing.prototype = { holy: 'object', batman: '!' };
Thing.prototype.constructor = Thing;
var Rock = function () {
this.hard = 'very';
};
Rock.prototype = new Thing();
Rock.constructor = Rock;
var newRock = new Rock();
var otherRock = Object.create(Object.prototype);
Rock.call(otherRock);
newRock.hard // => 'very'
otherRock.hard // => 'very'
newRock.hasMass // => true
otherRock.hasMass // => undefined
newRock.holy // => 'object'
otherRock.holy // => undefined
newRock instanceof Thing // => true
otherRock instanceof Thing // => false
Widzimy więc, że wywołanie Rock.call(otherRock)
nie powoduje dziedziczenia otherRock
z prototypu. Musi to uwzględniać przynajmniej niektóre z dodanych powolności. Chociaż w moich testach konstrukcja new
jest prawie 30 razy wolniejsza, nawet w tym prostym przykładzie.
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
2011-06-23 15:41:21
new f;
- take local function 'f' (access by indeks w ramce lokalnej) - tanio.
- execute bytecode BC_NEW_OBJECT (or coś w tym stylu) - tanio.
- wykonaj funkcję-tanie tutaj.
Teraz to:
g.call(Object.create(Object.prototype));
- Znajdź global var
Object
- tanio? - Znajdź właściwość
prototype
w Object-so-so - Znajdź właściwość
create
W Object-so-so - Find local var g; - cheap
- Find property
call
- so-so - Invoke
create
function-so-so - Invoke
call
function-so-so
I to:
new (function() { })
- tworzenie nowego obiektu function (tej anonimowej funkcji) - stosunkowo drogie.
- execute bytecode BC_NEW_OBJECT - tanie
- wykonaj funkcję-tanie tutaj.
Jak widzisz przypadek # 1 jest najmniej zużywający.
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
2011-06-24 21:26:34