var toSizeString = (function() {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
return function(size) {
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
})();
I szybsza funkcja: (należy pamiętać, że zawsze musi wyliczać ciągle te same zmienne kb / mb / gb). Gdzie zyskuje wydajność?
function toSizeString (size) {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
javascript
performance
Do mojego
źródło
źródło
Odpowiedzi:
Nowoczesne silniki JavaScript wykonują kompilację just-in-time. Nie możesz domniemywać, co „musi tworzyć w kółko”. W obu przypadkach tego rodzaju obliczenia są stosunkowo łatwe do optymalizacji.
Z drugiej strony, zamykanie stałych zmiennych nie jest typowym przypadkiem, dla którego celowałbyś w kompilację JIT. Zwykle tworzysz zamknięcie, gdy chcesz mieć możliwość zmiany tych zmiennych przy różnych wywołaniach. Tworzysz również dodatkowe dereferencje wskaźnika, aby uzyskać dostęp do tych zmiennych, na przykład różnicę między dostępem do zmiennej członka a lokalną int w OOP.
Tego rodzaju sytuacja powoduje, że ludzie odrzucają linię „przedwczesnej optymalizacji”. Łatwe optymalizacje są już wykonywane przez kompilator.
źródło
Zmienne są tanie. Konteksty wykonania i łańcuchy zasięgu są drogie.
Istnieją różne odpowiedzi, które zasadniczo sprowadzają się do „ponieważ zamknięcia”, i są one w istocie prawdą, ale problem nie dotyczy konkretnie zamknięcia, to fakt, że masz funkcję odwołującą się do zmiennych w innym zakresie.
window
Miałbyś ten sam problem, gdyby były to zmienne globalne na obiekcie, w przeciwieństwie do zmiennych lokalnych wewnątrz IIFE. Wypróbuj i przekonaj się.Więc w pierwszej funkcji, gdy silnik zobaczy to zdanie:
Musi podjąć następujące kroki:
size
w bieżącym zakresie. (Znalazłem.)GB
w bieżącym zakresie. (Nie znaleziono.)GB
w zakresie nadrzędnym. (Znalazłem.)gbSize
.Krok 3 jest znacznie droższy niż zwykła alokacja zmiennej. Co więcej, robisz to pięć razy , w tym dwa razy dla obu
GB
iMB
. Podejrzewam, że jeśli aliowałeś je na początku funkcji (np.var gb = GB
) I odwoływałeś się do aliasu, faktycznie spowodowałoby to niewielkie przyspieszenie, chociaż możliwe jest również, że niektóre silniki JS już przeprowadzają tę optymalizację. Oczywiście najskuteczniejszym sposobem na przyspieszenie wykonania jest po prostu wcale nie przechodzenie przez łańcuch zasięgu.Należy pamiętać, że JavaScript nie jest jak skompilowany, statycznie typowany język, w którym kompilator rozwiązuje te zmienne adresy w czasie kompilacji. Silnik JS musi rozwiązać je według nazwy , a te wyszukiwania mają miejsce za każdym razem w środowisku wykonawczym. Więc chcesz ich unikać, kiedy to możliwe.
Przypisywanie zmiennych w JavaScript jest wyjątkowo tanie. To może być najtańsza operacja, chociaż nie mam nic na poparcie tego stwierdzenia. Niemniej jednak można śmiało powiedzieć, że prawie nigdy nie jest dobrym pomysłem unikanie tworzenia zmiennych; prawie każda optymalizacja, którą spróbujesz zrobić w tym obszarze, ostatecznie pogorszy sytuację, jeśli chodzi o wydajność.
źródło
var a, b, c
że możemy uzyskać dostępb
jakoscope[1]
. Wszystkie zakresy są ponumerowane, a jeśli zakres ten jest zagnieżdżony na pięciu zakresach, wówczasb
jest w pełni adresowany,env[5][1]
co jest znane podczas analizowania. W natywnym kodzie zakresy odpowiadają segmentom stosu. Zamknięcia są bardziej skomplikowane, gdyż muszą one wykonać kopię zapasową i zastąpićenv
*(scope->outer + variable_offset)
za dostęp; każdy poziom zakresu funkcji dodatkowej kosztuje jedno dodatkowe odchylenie wskaźnika. Wygląda na to, że oboje mieliśmy rację :)Jeden przykład dotyczy zamknięcia, drugi nie. Wdrożenie zamknięć jest dość trudne, ponieważ zmienne zamknięte nad zmiennymi nie działają jak zmienne normalne. Jest to bardziej oczywiste w języku niskiego poziomu, takim jak C, ale zilustruję to JavaScript.
Zamknięcie składa się nie tylko z funkcji, ale także ze wszystkich zmiennych, które zamknął. Kiedy chcemy wywołać tę funkcję, musimy również podać wszystkie zmienne zamknięte. Możemy modelować zamknięcie za pomocą funkcji, która otrzymuje obiekt jako pierwszy argument reprezentujący te zamknięte zmienne:
Zwróć uwagę na niezręczną konwencję wywoływania, która
closure.apply(closure, ...realArgs)
tego wymagaObsługa wbudowanych skryptów JavaScript umożliwia pominięcie jawnego
vars
argumentu i pozwalathis
zamiast tego użyć :Te przykłady są równoważne z tym kodem, który faktycznie używa zamknięć:
W tym ostatnim przykładzie obiekt służy tylko do grupowania dwóch zwróconych funkcji;
this
wiązania nie ma znaczenia. Wszystkie szczegóły umożliwiające zamknięcie - przekazywanie ukrytych danych do faktycznej funkcji, zmiana wszystkich dostępów do zmiennych zamknięcia na wyszukiwanie w tych ukrytych danych - są obsługiwane przez język.Ale wywoływanie zamknięć wiąże się z narzutem związanym z przekazywaniem tych dodatkowych danych, a uruchomienie zamknięcia wiąże się z narzutem związanym z wyszukiwaniem tych dodatkowych danych - pogarszanym przez złą lokalizację pamięci podręcznej i zwykle dereferencję wskaźnika w porównaniu ze zwykłymi zmiennymi - więc nie jest zaskakujące, że rozwiązanie, które nie opiera się na zamknięciach, działa lepiej. Zwłaszcza, że wszystko, co oszczędza ci zamknięcie, to kilka wyjątkowo tanich operacji arytmetycznych, które mogą być nawet stale składane podczas analizowania.
źródło