Czy pętle są naprawdę szybsze do tyłu?

268

Słyszałem to już kilka razy. Czy pętle JavaScript są naprawdę szybsze podczas liczenia wstecz? Jeśli tak, to dlaczego? Widziałem kilka przykładów zestawu testów pokazujących, że odwrócone pętle są szybsze, ale nie mogę znaleźć żadnego wyjaśnienia, dlaczego!

Zakładam, że dzieje się tak, ponieważ pętla nie musi już oceniać właściwości za każdym razem, gdy sprawdza, czy jest zakończona, a jedynie sprawdza ostateczną wartość liczbową.

To znaczy

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}
djdd87
źródło
6
hehe. zajmie to w nieskończoność. spróbuj i--
StampedeXV
5
forPętla wsteczna jest szybsza, ponieważ zmienna sterująca pętli górnej granicy (hehe, dolnej granicy) nie musi być definiowana ani pobierana z obiektu; jest to stałe zero.
Josh Stodola
88
Nie ma prawdziwej różnicy . Natywne konstrukcje pętli zawsze będą bardzo szybkie . Nie martw się o ich wydajność.
James Allardice,
24
@Afshin: W przypadku takich pytań pokaż nam artykuły, o których mowa.
Wyścigi lekkości na orbicie
22
Różnica jest szczególnie ważna w przypadku urządzeń o bardzo niskim zużyciu energii i zasilanych bateryjnie. Różnice polegają na tym, że z i-- porównujesz do 0 na końcu pętli, podczas gdy z i ++ porównujesz z liczbą> 0. Uważam, że różnica wydajności wynosiła około 20 nanosekund (coś jak axp axp, 0 vs. cmp axe , bx) - co jest niczym, ale jeśli
zapętlasz

Odpowiedzi:

902

To nie tak, że i--jest szybszy niż i++. W rzeczywistości oba są równie szybkie.

To, co wymaga czasu w rosnących pętlach, ocenia dla każdej iwielkości rozmiar tablicy. W tej pętli:

for(var i = array.length; i--;)

Oceniasz .lengthtylko raz, kiedy deklarujesz i, podczas gdy dla tej pętli

for(var i = 1; i <= array.length; i++)

oceniasz za .lengthkażdym razem i, gdy zwiększasz , sprawdzając, czy i <= array.length.

W większości przypadków nie powinieneś nawet martwić się o tego rodzaju optymalizację .

Alestanis
źródło
23
czy warto wprowadzić zmienną dla array.length i użyć jej w głowie pętli for?
ragatskynet
39
@ragatskynet: Nie, chyba że konfigurujesz test porównawczy i chcesz coś zrobić.
Jon
29
@ragatskynet To zależy: czy szybciej będzie oceniać .lengthokreśloną liczbę razy, czy też deklarować i definiować nową zmienną? W większości przypadków jest to przedwczesna (i błędna) optymalizacja, chyba że lengthocena jest bardzo droga.
alestanis
10
@ Dr.Dredel: To nie jest porównanie - to ocena. 0jest szybszy do oceny niż array.length. Cóż, podobno.
Wyścigi lekkości na orbicie
22
Warto wspomnieć, że mówimy o językach interpretowanych, takich jak Ruby, Python. Języki skompilowane, np. Java, mają optymalizacje na poziomie kompilatora, które „wygładzą” te różnice do tego stopnia, że ​​nie ma znaczenia, czy .lengthjest to deklaracja, for loopczy nie.
Haris Krajina
198

Ten facet porównał wiele pętli w javascript, w wielu przeglądarkach. Ma także zestaw testowy więc możesz sam je uruchomić.

We wszystkich przypadkach (chyba że brakowało mi jednego z moich odczytów) najszybsza pętla to:

var i = arr.length; //or 10
while(i--)
{
  //...
}
Tom Ritter
źródło
Fajnie :) Ale nie ma wstecznego testu „dla” pętli… Ale pętle for wspomniane przez peirix lub Searlea powinny być prawie takie same jak pętla „while” z „i-” jako warunkiem. To była najszybsza przetestowana pętla.
SvenFinke
11
Ciekawe, ale zastanawiam się, czy wstępne zmniejszenie byłoby jeszcze szybsze. Ponieważ nie będzie musiał przechowywać wartości pośredniej i.
tvanfosson
Jeśli dobrze pamiętam, mój profesor kursu sprzętowego powiedział, że test na 0 lub nie na 0 jest najszybszym możliwym „obliczeniem”. Podczas gdy (i--) test jest zawsze testem na 0. Może dlatego jest to najszybsze?
StampedeXV
3
@tvanfosson, jeśli wcześniej dokonałeś dekrementacji --i, musisz użyć var current = arr[i-1];wewnątrz pętli lub będzie ona wyłączona przez jeden ...
DaveAlger
2
Słyszałem, że i-- może być szybszy niż --i, ponieważ w drugim przypadku procesor musi się zmniejszyć, a następnie przetestować pod kątem nowej wartości (między instrukcjami występuje zależność danych), podczas gdy w pierwszym przypadku procesor może przetestuj istniejącą wartość i zmniejsz ją później. Nie jestem pewien, czy dotyczy to JavaScript, czy tylko bardzo niskiego poziomu kodu C.
marcus
128

Próbuję dać szeroki obraz tej odpowiedzi.

Poniższe myśli w nawiasach było moim przekonaniem, dopóki nie przetestowałem tego problemu:

[[W odniesieniu do języków niskiego poziomu, takich jak C / C ++ , kod jest kompilowany, dzięki czemu procesor ma specjalne polecenie skoku warunkowego, gdy zmienna ma wartość zero (lub niezerową).
Ponadto, jeśli zależy ci na takiej optymalizacji, możesz ++izamiast tego przejść i++, ponieważ ++ijest to polecenie jednego procesora, a jednocześnie i++oznaczaj=i+1, i=j .]]

Naprawdę szybkie pętle można wykonać, rozwijając je:

for(i=800000;i>0;--i)
    do_it(i);

Może być znacznie wolniejszy niż

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

ale przyczyny tego mogą być dość skomplikowane (aby wspomnieć, istnieją problemy związane z przetwarzaniem poleceń procesora i obsługą pamięci podręcznej w grze).

Pod względem języków wysokiego poziomu , takich jak JavaScript o które prosiłeś, możesz zoptymalizować rzeczy, jeśli polegasz na bibliotekach, wbudowanych funkcjach zapętlania. Pozwól im zdecydować, jak najlepiej to zrobić.

W związku z tym w JavaScript sugerowałbym użycie czegoś takiego

array.forEach(function(i) {
    do_it(i);
});

Jest również mniej podatny na błędy, a przeglądarki mają szansę zoptymalizować kod.

[UWAGA: nie tylko przeglądarki, ale także masz przestrzeń do łatwej optymalizacji, po prostu ponownie zdefiniuj forEachfunkcję (zależnie od przeglądarki), aby korzystała z najnowszych najlepszych sztuczek! :) @AMK mówi w szczególnych przypadkach, że warto raczej użyć array.poplub array.shift. Jeśli to zrobisz, połóż go za zasłoną. Największą nadwyżką jest dodanie opcji forEachwyboru algorytmu zapętlenia.]

Ponadto, również w przypadku języków niskiego poziomu, najlepszą praktyką jest użycie inteligentnej funkcji bibliotecznej do złożonych, zapętlonych operacji, jeśli jest to możliwe.

Te biblioteki mogą również umieszczać różne elementy (wielowątkowe) za twoimi plecami, a także wyspecjalizowani programiści utrzymują je na bieżąco.

Zrobiłem trochę więcej kontroli i okazuje się, że w C / C ++, nawet dla operacji 5e9 = (50 000 x 100 000), nie ma różnicy między przechodzeniem w górę i w dół, jeśli testowanie odbywa się na stałej, jak mówi @alestanis. (Wyniki JsPerf są czasami niespójne, ale ogólnie rzecz biorąc mówią to samo: nie można zrobić dużej różnicy.)
Tak więc --izdarza się, że jest to raczej „elegancka” rzecz. To tylko sprawia, że ​​wyglądasz jak lepszy programista. :)

Z drugiej strony, za rozwinięcie w tej sytuacji 5e9, zmniejszyłem mnie z 12 sekund do 2,5 sekundy, gdy przekroczyłem 10 sekund, i do 2,1 sekundy, kiedy przekroczyłem 20s. To było bez optymalizacji, a optymalizacja sprowadziła rzeczy do niezmierzonego krótkiego czasu. :) (Rozwijanie można wykonać na swój sposób powyżej lub przy użyciu i++, ale to nie przyspiesza działania w JavaScript.)

Podsumowując: zachowaj i--/ i++i ++i/ i++różnice w rozmowach kwalifikacyjnych, trzymaj się array.forEachlub innych złożonych funkcji bibliotecznych, jeśli są dostępne. ;)

Barney
źródło
18
Kluczowym słowem jest „może być”. Twoja rozwinięta pętla może być wolniejsza niż oryginalna. Podczas optymalizacji zawsze dokonuj pomiaru, abyś dokładnie wiedział, jaki wpływ miały twoje zmiany.
lipiec
1
@jalf true true, +1. Różne długości bez pętli (> = 1) różnią się wydajnością. Dlatego wygodniej jest pozostawić to zadanie bibliotekom, jeśli to możliwe, nie wspominając o tym, że przeglądarki działają na różnych architekturach, więc może być lepiej, jeśli zdecydują, jak to zrobić array.each(...). Nie sądzę, żeby próbowali eksperymentować z rozpętaniem zwykłych pętli for.
Barney
1
@BarnabasSzabolcs Pytanie dotyczy konkretnie JavaScript, a nie C lub innych języków. W JS jest różnica, proszę zobaczyć moją odpowiedź poniżej. Chociaż nie dotyczy pytania, +1 dobra odpowiedź!
AMK
1
I proszę bardzo - +1zdobyć Great Answerodznakę. Naprawdę świetna odpowiedź. :)
Rohit Jain
1
APL / A ++ też nie jest językiem. Ludzie używają ich, aby wyrazić, że nie są zainteresowani specyfiką języka, ale czymś, co łączy te języki.
Barney
33

i-- jest tak szybki jak i++

Poniższy kod jest tak szybki jak Twój, ale używa dodatkowej zmiennej:

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

Zaleca się, aby NIE oceniać wielkości tablicy za każdym razem. W przypadku dużych tablic widać spadek wydajności.

sainiuc
źródło
6
Po prostu się tutaj mylisz. Teraz kontrola pętli wymaga dodatkowej zmiennej wewnętrznej (i i więcej), a w zależności od silnika JavaScript może to ograniczyć potencjał optymalizacji kodu. JIT przetłumaczy proste pętle na bezpośrednie kody maszynowe, ale jeśli pętle mają zbyt wiele zmiennych, JIT nie będzie w stanie zoptymalizować tak dobrze. Zasadniczo jest to ograniczone przez architekturę lub rejestry procesora, których używa JIT. Inicjalizacja raz i zejście po prostu najczystsze rozwiązanie i dlatego jest zalecane wszędzie.
Pavel P
Dodatkowa zmienna zostanie wyeliminowana przez optymalizator wspólnego interpretera / kompilatora. Chodzi o to „porównaj z 0” - dalsze wyjaśnienia znajdziesz w mojej odpowiedzi.
H.-Dirk Schmitt
1
Lepiej umieścić „wewnątrz” w pętli:for (var i=0, up=Things.length; i<up; i++) {}
thdoan
22

Ponieważ jesteś zainteresowany tym tematem, zapoznaj się z postem na blogu Grega Reimera na temat testu porównawczego pętli JavaScript. Jaki jest najszybszy sposób na kodowanie pętli w JavaScript? :

Zbudowałem zestaw testów porównawczych dla różnych sposobów kodowania pętli w JavaScript. Jest ich już kilka, ale nie znalazłem żadnej, która potwierdzałaby różnicę między macierzami macierzystymi a kolekcjami HTML.

Możesz także wykonać test wydajności w pętli , otwierając https://blogs.oracle.com/greimer/resource/loop-test.html(nie działa, jeśli JavaScript jest zablokowany w przeglądarce na przykład przez NoScript ).

EDYTOWAĆ:

Nowszy test porównawczy stworzony przez Milana Adamowskiego można wykonać tutaj w czasie wykonywania dla różnych przeglądarek.

Do testowania w przeglądarce Firefox 17.0 na Mac OS X 10.6 dostałem następującą pętlę:

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

najszybciej jak poprzedza:

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

Przykład testu porównawczego.

Dreamcrash
źródło
5
Ten post ma teraz 4 lata. Biorąc pod uwagę szybkość (ha!), Z jaką silniki JavaScript zostały zaktualizowane, większość porad może być nieaktualna - artykuł nawet nie wspomina o Chrome i jest błyszczącym silnikiem JavaScript V8.
Yi Jiang
Tak, tęsknię za tym szczegółem, dziękuję za zwrócenie na to uwagi. Zdarzenie w tym czasie nie było dużej różnicy między --i i i ++ w pętli dla.
dreamcrash
19

To nie jest --lub ++jest to porównać działanie. Ze --można użyć porównania z 0, natomiast ze ++trzeba porównać ją z długością. W procesorze porównanie z zerem jest zwykle dostępne, natomiast porównanie z skończoną liczbą całkowitą wymaga odjęcia.

a++ < length

jest właściwie skompilowany jako

a++
test (a-length)

Po skompilowaniu zajmuje to więcej czasu procesorowi.

Dr BDO Adams
źródło
15

Krótka odpowiedź

W przypadku normalnego kodu, szczególnie w języku wysokiego poziomu, takim jak JavaScript , nie ma różnicy w wydajności w i++i i--.

Kryterium wydajności to użycie w forpętli i porównanie .

To dotyczy wszystkich języków wysokiego poziomu , a to przede wszystkim niezależny od stosowania JavaScript. Wyjaśnieniem jest wynikowy kod asemblera w dolnej linii.

Szczegółowe wyjaśnienie

Różnica wydajności może wystąpić w pętli. Tło jest takie, że na poziomie kodu asemblera widać, że:compare with 0 to tylko jedna instrukcja, która nie wymaga dodatkowego rejestru.

To porównanie jest wydawane przy każdym przejściu pętli i może spowodować wymierną poprawę wydajności.

for(var i = array.length; i--; )

zostanie przetworzony na pseudo kod taki jak ten:

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

Zauważ, że 0 jest dosłowne lub innymi słowy stałą wartością.

for(var i = 0 ; i < array.length; i++ )

zostanie przetworzony na pseudo kod taki jak ten (zakłada się normalną optymalizację interpretera):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

Zauważ, że end jest zmienną wymagającą rejestru procesora . Może to wywołać dodatkową zamianę rejestru w kodzie i wymaga droższej instrukcji porównania w ifinstrukcji.

Tylko moje 5 centów

W przypadku języka wysokiego poziomu czytelność, która ułatwia konserwację, jest ważniejsza jako niewielka poprawa wydajności.

Zwykle lepsza jest klasyczna iteracja od początku do końca tablicy .

Szybsza iteracja od końca tablicy do początku powoduje prawdopodobnie niepożądaną odwróconą sekwencję.

Post Scriptum

Jak zadano w komentarzu: Różnica --ii i--jest w oceniei przed lub po zmniejszanie.

Najlepszym wyjaśnieniem jest wypróbowanie ;-) Oto przykład Bash .

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9
H.-Dirk Schmitt
źródło
1
1+ Dobre wyjaśnienie. Tylko pytanie trochę poza zakresem, czy mógłbyś wyjaśnić różnicę między --ia i--także?
Afshin Mehrabani
14

Widziałem to samo zalecenie w Sublime Text 2.

Jak już powiedziano, głównym ulepszeniem nie jest ocena długości tablicy przy każdej iteracji w pętli for. Jest to dobrze znana technika optymalizacji i szczególnie wydajna w JavaScript, gdy tablica jest częścią dokumentu HTML (robi tofor dla wszystkichli elementów).

Na przykład,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

jest znacznie wolniejszy niż

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

Z tego, co stoję, główną poprawą formy w pytaniu jest to, że nie deklaruje dodatkowej zmiennej (len w moim przykładzie)

Ale jeśli mnie zapytasz, cała sprawa nie polega na optymalizacji i++vs i--, ale na tym, że nie musisz oceniać długości tablicy przy każdej iteracji (możesz zobaczyć test porównawczy na jsperf ).

BBog
źródło
1
Muszę zrobić wyjątek od słowa obliczającego tutaj. Zobacz mój komentarz do odpowiedzi Pavela . Specyfikacja ECMA stwierdza, że ​​długość tablicy nie jest obliczana, gdy się do niej odwołujesz.
kojiro
Czy „ocena” byłaby lepszym wyborem? Ciekawostka faktem, nie wiedziałem o tym
BBog
Dodatkowa zmienna zostanie wyeliminowana przez optymalizator wspólnego interpretera / kompilatora. To samo dotyczy oceny tablicy. Długość. Chodzi o to „porównaj z 0” - dalsze wyjaśnienia znajdziesz w mojej odpowiedzi.
H.-Dirk Schmitt
@ H.-DirkSchmitt odłóż na bok swoją rozsądną odpowiedź, przez długi czas w historii JavaScript kompilator nie optymalizował kosztów wydajności łańcucha wyszukiwania. AFAIK V8 jako pierwszy próbował.
kojiro
@ H.-DirkSchmitt, tak jak powiedział Kojiro, jest to znana i dobrze znana sztuczka. Nawet jeśli nie jest już istotny w nowoczesnych przeglądarkach, nadal nie czyni go przestarzałym. Poza tym, robiąc to albo z wprowadzeniem nowej zmiennej długości, albo ze sztuczką w pytaniu OP, jest to nadal najlepszy sposób, imo. To po prostu mądra rzecz i dobra praktyka, nie sądzę, żeby miało to coś wspólnego z faktem, że kompilator został zoptymalizowany, aby zająć się czymś, co często robi się źle w JS
BBog
13

Nie sądzę, że sensownie jest powiedzieć, że i--jest szybszy niż i++w JavaScript.

Przede wszystkim zależy to całkowicie od implementacji silnika JavaScript.

Po drugie , pod warunkiem, że najprostsze konstrukcje JIT i przetłumaczone na instrukcje natywne, wtedy i++vs i--będzie całkowicie zależeć od procesora, który je wykonuje. Oznacza to, że na ARM (telefony komórkowe) szybciej jest zejść do zera, ponieważ zmniejszanie i porównywanie do zera są wykonywane w jednej instrukcji.

Prawdopodobnie myślałeś, że jeden był lepszy niż drugi, ponieważ tak sugeruje

for(var i = array.length; i--; )

ale sugerowany sposób nie polega na tym, że jeden jest szybszy od drugiego, ale po prostu dlatego, że piszesz

for(var i = 0; i < array.length; i++)

następnie przy każdej iteracji array.lengthtrzeba było oceniać (być może mądrzejszy silnik JavaScript mógłby stwierdzić, że pętla nie zmieni długości tablicy). Mimo że wygląda na prostą instrukcję, w rzeczywistości jest to funkcja wywoływana pod maską przez silnik JavaScript.

Innym powodem, dla i--którego można uznać to za „szybsze”, jest to, że silnik JavaScript musi przydzielić tylko jedną zmienną wewnętrzną do sterowania pętlą (zmienną do var i). W porównaniu do array.length lub jakiejś innej zmiennej, musiała istnieć więcej niż jedna zmienna wewnętrzna do sterowania pętlą, a liczba zmiennych wewnętrznych jest ograniczoną zaletą silnika JavaScript. Im mniej zmiennych jest używanych w pętli, tym większa szansa, że ​​JIT ma szansę na optymalizację. Dlatego i--można go uznać za szybszy ...

Pavel P.
źródło
3
Prawdopodobnie warto ostrożnie sformułować sposób array.lengthoceny. Długość nie jest obliczana, gdy się do niej odwołujesz. (To tylko właściwość, która jest ustawiana za każdym razem, gdy indeks tablicy jest tworzony lub zmieniany ). Gdy występują dodatkowe koszty, dzieje się tak, ponieważ silnik JS nie zoptymalizował łańcucha wyszukiwania dla tej nazwy.
kojiro
Cóż, nie jestem pewien, co mówi specyfikacja Ecma, ale wiedza o niektórych elementach wewnętrznych różnych silników JavaScript nie jest prosta, getLength(){return m_length; }ponieważ wiąże się to z utrzymaniem porządku. Ale jeśli spróbujesz pomyśleć wstecz: byłoby całkiem genialnie napisać implementację tablicy, w której długość musiałaby być rzeczywiście obliczona :)
Pavel P
specyfikacja ECMA wymaga, aby właściwość długości była już obliczona. lengthMusi być aktualizowana natychmiast, gdy właściwość-that-is-an-array-indeks jest dodane lub zmienione.
kojiro
Próbuję powiedzieć, że bardzo trudno jest naruszyć specyfikację, jeśli spróbujesz o tym pomyśleć.
Pavel P
1
Pod tym względem x86 jest jak ARM. dec/jnzvs inc eax / cmp eax, edx / jne.
Peter Cordes,
13

Ponieważ żadna z pozostałych odpowiedzi nie wydaje się odpowiadać na konkretne pytanie (ponad połowa z nich pokazuje przykłady C i omawia języki niższego poziomu, twoje pytanie dotyczy JavaScript), postanowiłem napisać własny.

Więc proszę:

Prosta odpowiedź: i-- jest na ogół szybsza, ponieważ nie musi uruchamiać porównania do 0 za każdym razem, wyniki testów dla różnych metod są poniżej:

Wyniki testu: „Jak udowodniono” w tym jsPerf, arr.pop()jest to jak dotąd najszybsza pętla. Ale, koncentrując się na --i, i--, i++a ++ijak pytasz w swoim pytaniu, oto jsPerf (są z wielu jsPerf'S, patrz źródła poniżej) Wyniki podsumowane:

--ii i--są takie same w Firefox, podczas gdy i--jest szybszy w Chrome.

W Chrome podstawowym pętli for ( for (var i = 0; i < arr.length; i++)) jest szybsza niż i--i --ipodczas gdy Firefox jest wolniejszy.

Zarówno w Chrome, jak i Firefox pamięć podręczna arr.lengthjest znacznie szybsza w Chrome o około 170 000 operacji na sekundę.

Bez znaczącej różnicy, ++ijest szybszy niż i++w większości przeglądarek, AFAIK, nigdy nie jest odwrotnie w żadnej przeglądarce.

Krótsze podsumowanie: arr.pop() jak dotąd najszybsza pętla; dla specjalnie wymienionych pętli, i--jest najszybszą pętlą.

Źródła: http://jsperf.com/fastest-array-loops-in-javascript/15 , http://jsperf.com/ipp-vs-ppi-2

Mam nadzieję, że to odpowiada na twoje pytanie.

AMK
źródło
1
Wygląda na to, że twój poptest wydaje się dość szybki, ponieważ zmniejsza rozmiar tablicy do 0 dla większości pętli - o ile wiem. Jednak, aby upewnić się, że wszystko jest w porządku, zaprojektowałem ten jsperf, aby tworzył tablicę w taki sam sposób przy każdym teście - który wydaje się .shift()być zwycięzcą dla moich kilku przeglądarek - ale nie tego bym się spodziewał :) jsperf.com /
Compare
Wybitny za to, że jako jedyny wspomniał ++i: D
jaggedsoft
12

Zależy to od umieszczenia tablicy w pamięci i współczynnika trafień stron pamięci podczas uzyskiwania dostępu do tej tablicy.

W niektórych przypadkach dostęp do elementów tablicy w kolejności kolumn jest szybszy niż w kolejności wierszy ze względu na wzrost współczynnika trafień.

Akbar Bayati
źródło
1
Gdyby tylko OP zapytał, dlaczego przemierzanie tej samej matrycy w różnych zamówieniach może zająć różną ilość czasu ...
Moskwa
1
Biorąc pod uwagę, że w systemach operacyjnych zarządzanie pamięcią opiera się na stronicowaniu, gdy proces potrzebuje danych, które nie znajdują się na stronach buforowanych, występuje błąd strony w systemie operacyjnym i musi on doprowadzić stronę docelową do pamięci podręcznej procesora i zastąpić inną stroną, powoduje narzut przetwarzania, który zajmuje więcej czasu niż gdy strona docelowa znajduje się w pamięci podręcznej procesora. Załóżmy, że zdefiniowaliśmy dużą tablicę, w której każdy wiersz w niej jest większy niż rozmiar systemu operacyjnego strony i uzyskujemy do niej dostęp w kolejności wierszy, w tym przypadku współczynnik błędów strony wzrasta, a wynik jest wolniejszy niż dostęp do kolejności kolumn w tej tablicy.
Akbar Bayati
Błędy strony to nie to samo, co brak pamięci podręcznej. Błąd strony występuje tylko wtedy, gdy pamięć została przeniesiona na dysk. Dostęp do tablic wielowymiarowych w kolejności sekwencyjnej sposobu ich przechowywania w pamięci jest szybszy ze względu na lokalizację pamięci podręcznej (użycie wszystkich bajtów linii pamięci podręcznej po jej pobraniu), a nie z powodu błędów strony. (chyba że zestaw roboczy jest za duży dla używanego komputera).
Peter Cordes
10

Ostatni raz o tym myślałem podczas pisania zestawu 6502 (8 bitów, tak!). Dużym zyskiem jest to, że większość operacji arytmetycznych (zwłaszcza dekrementów) aktualizuje zestaw flag, jedną z nich był Zwskaźnik „osiągnął zero”.

Tak więc na końcu pętli wykonałeś dwie instrukcje: DEC(zmniejszenie) i JNZ(skok, jeśli nie zero), porównanie nie jest potrzebne!

Javier
źródło
W przypadku JavaScript oczywiście nie ma zastosowania (ponieważ działa na procesorach, które nie mają takich kodów operacyjnych). Najprawdopodobniej prawdziwym powodem i--vs i++jest to, że w pierwszym przypadku nie wprowadza się dodatkowych zmiennych kontrolnych w zakresie pętli. Zobacz moją odpowiedź poniżej ...
Pavel P
1
tak, to naprawdę nie ma zastosowania; ale jest to bardzo powszechny styl C i wygląda na czystsze dla tych z nas, którzy się do tego przyzwyczaili. :-)
Javier,
Zarówno x86, jak i ARM są pod tym względem jak 6502. dec/jnzzamiast inc/cmp/jnedla przypadku x86. Nie zobaczysz, że pusta pętla działa szybciej (oba nasycą przepustowość gałęzi), ale odliczanie nieznacznie zmniejsza obciążenie pętli. Obecne moduły wstępnego Intel Intel nie przeszkadzają wzorce dostępu do pamięci, które idą w kolejności malejącej. Myślę, że starsze procesory mogłyby śledzić 4 strumienie wstecz i 6 lub 10 strumieni w przód, IIRC.
Peter Cordes,
7

Można to wyjaśnić JavaScriptem (i wszystkimi językami), które ostatecznie zamieniają się w kody operacyjne do uruchomienia na CPU. Procesory zawsze mają jedną instrukcję porównywania z zerem, co jest cholernie szybkie.

Nawiasem mówiąc, jeśli możesz zagwarantować , że countzawsze >= 0, możesz uprościć:

for (var i = count; i--;)
{
  // whatever
}
Searlea
źródło
1
Krótszy kod źródłowy niekoniecznie oznacza, że ​​będzie szybszy. Zmierzyłeś to?
Greg Hewgill
Ups, tęskniłem za tym. Gwóźdź na głowie, chłopie.
Robert,
1
Chciałbym zobaczyć źródła asemblera, w których porównanie z 0 jest inne. Minęło kilka lat, ale kiedyś zrobiłem mnóstwo kodowania asemblacyjnego i przez całe życie nie mogę wymyślić sposobu na porównanie / testowanie z 0 w sposób, który nie może być równie szybki dla dowolnej innej liczby całkowitej. Jednak to, co mówisz, brzmi prawdziwie. Sfrustrowany, że nie mogę zrozumieć, dlaczego!
Brian Knoblauch
7
@Brian Knoblauch: Jeśli użyjesz instrukcji takiej jak „dec eax” (kod x86), wówczas instrukcja ta automatycznie ustawia flagę Z (zero), którą możesz natychmiast przetestować bez konieczności używania innej instrukcji porównywania pomiędzy nimi.
Greg Hewgill
1
Przy interpretacji Javascript wątpię jednak, aby wąskie gardło stanowiło kody operacyjne. Bardziej prawdopodobne jest, że mniej tokenów oznacza, że ​​interpreter może szybciej przetwarzać kod źródłowy.
Todd Owen
6

Nie zależy to od znaku --lub ++, ale zależy od warunków stosowanych w pętli.

Na przykład: Twoja pętla jest szybsza, jeśli zmienna ma wartość statyczną, niż wtedy, gdy pętla sprawdza warunki za każdym razem, takie jak długość tablicy lub inne warunki.

Ale nie martw się o tę optymalizację, ponieważ tym razem jej działanie jest mierzone w nanosekundach.

Arvind Kanjariya
źródło
wielokrotne pętle nanosekundowe mogą stać się sekundami ... optymalizacja nigdy nie jest złym pomysłem, gdy masz na to czas
Buksy
6

for(var i = array.length; i--; )nie jest dużo szybszy. Ale po wymianie array.lengthz super_puper_function(), które mogą być znacznie szybciej (ponieważ to się nazywa w każdej iteracji). To jest różnica.

Jeśli zamierzasz to zmienić w 2014 roku, nie musisz myśleć o optymalizacji. Jeśli zamierzasz to zmienić za pomocą „Wyszukaj i zamień”, nie musisz myśleć o optymalizacji. Jeśli nie masz czasu, nie musisz myśleć o optymalizacji. Ale teraz masz czas, aby się nad tym zastanowić.

PS: i--nie jest szybszy niż i++.

Dmitrij Isaev
źródło
6

Krótko mówiąc: nie ma absolutnie żadnej różnicy w robieniu tego w JavaScript.

Przede wszystkim możesz to przetestować samodzielnie:

Nie tylko możesz przetestować i uruchomić dowolny skrypt w dowolnej bibliotece JavaScript, ale masz również dostęp do całej gamy wcześniej napisanych skryptów, a także możesz zobaczyć różnice między czasem wykonania w różnych przeglądarkach na różnych platformach.

O ile widać, nie ma różnicy między wydajnością w dowolnym środowisku.

Jeśli chcesz poprawić wydajność skryptu, możesz spróbować:

  1. Przygotuj var a = array.length;instrukcję, aby nie obliczać jej wartości za każdym razem w pętli
  2. Czy rozwijanie pętli http://en.wikipedia.org/wiki/Loop_unwinding

Musisz jednak zrozumieć, że poprawa, którą możesz uzyskać, będzie tak nieznaczna, że ​​przeważnie nie powinieneś się tym przejmować.

Moja własna opinia, dlaczego pojawiło się takie nieporozumienie (Dec vs. Inc)

Dawno, dawno temu istniała wspólna instrukcja maszyny, DSZ (Decrement and Skip on Zero). Ludzie, którzy programowali w asemblerze, używali tej instrukcji do implementacji pętli w celu zapisania rejestru. Teraz te starożytne fakty są przestarzałe i jestem prawie pewien, że nie uzyskasz żadnej poprawy wydajności w żadnym języku za pomocą tej pseudopoprawki.

Myślę, że jedynym sposobem, w jaki taka wiedza może się propagować w naszych czasach, jest czytanie kodu osoby innej osoby. Zobacz taką konstrukcję i zapytaj, dlaczego została wdrożona, a tutaj odpowiedź: „poprawia wydajność, ponieważ porównuje do zera”. Oszołomiłeś się wyższą wiedzą o swoim współpracowniku i myślisz, że możesz go wykorzystać, aby być mądrzejszym :-)

Salvador Dali
źródło
Ciekawe, ale dla twoich testów uruchomionych pod Firefox 16.0.2 na Win7, pętla dekrementacyjna była o 29% wolniejsza ... EDYCJA: Zignoruj ​​to. Powtarzające się testy okazały się niejednoznaczne, w moich testach dla kolejnych przebiegów jest zaskakująco dużo hałasu. Nie jestem pewien dlaczego.
Tak, próbowałem to wyjaśnić, zamykając wszystko inne i uruchamiając testy. Nadal mam dziwne wyniki. Bardzo dziwny.
Myślę, że przegapiłeś prawdziwy punkt, dla którego przejście do zera jest uważane za lepsze w JavaScript. Dzieje się tak głównie dlatego, że w ten sposób tylko jedna zmienna steruje wykonywaniem pętli, np. W ten sposób optymalizator / JITer ma więcej miejsca na ulepszenia. Korzystanie array.lengthniekoniecznie wiąże się z obniżeniem wydajności, po prostu dlatego, że maszyna wirtualna JS jest wystarczająco inteligentna, aby stwierdzić, czy tablica nie jest modyfikowana przez ciało pętli. Zobacz moją odpowiedź poniżej.
Pavel P
1
nitpicking: ten starożytny fakt (optymalizacja asemblera) nie jest przestarzały, tylko tajemny. jak w środku, nie musisz wiedzieć, chyba że naprawdę. :-)
Javier
6

Zrobiłem porównanie na jsbench .

Jak zauważył Alestani, jedną rzeczą, która wymaga czasu w rosnących pętlach, jest ocena, dla każdej iteracji, rozmiaru tablicy. W tej pętli:

for ( var i = 1; i <= array.length; i++ )

oceniasz za .lengthkażdym razem, gdy zwiększasz i. W tym:

for ( var i = 1, l = array.length; i <= l; i++ )

oceniasz .lengthtylko raz, kiedy deklarujesz i. W tym:

for ( var i = array.length; i--; )

porównanie jest niejawne, dzieje się to tuż przed zmniejszeniem i, a kod jest bardzo czytelny. Jednak to, co może zrobić niesamowitą różnicę, to to, co wkładasz do pętli.

Pętla z wywołaniem funkcji (zdefiniowanym gdzie indziej):

for (i = values.length; i-- ;) {
  add( values[i] );
}

Pętla z wbudowanym kodem:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

Jeśli możesz wstawić kod zamiast wywoływać funkcję bez utraty czytelności, możesz mieć pętlę rzędu wielkości szybciej!


Uwaga : ponieważ przeglądarka staje się dobra w wprowadzaniu prostych funkcji, tak naprawdę zależy to od stopnia skomplikowania kodu. Więc profiluj przed optymalizacją, ponieważ

  1. Wąskie gardło może być gdzie indziej (ajax, reflow, ...)
  2. Możesz wybrać lepszy algorytm
  3. Możesz wybrać lepszą strukturę danych

Ale pamiętaj:

Kod jest napisany dla ludzi do czytania i tylko przypadkowo dla maszyn do wykonania.

Spyryto
źródło
+1 za tę odpowiedź i za test porównawczy. Dodałem testy forEach i przerobiłem test porównawczy na samodzielny plik, który można uruchomić zarówno w przeglądarce, jak i w węźle. jsfiddle.net/hta69may/2 . W przypadku węzła „Odwrócona pętla, niejawne porównanie, wstawiony kod” jest najszybsza. Ale testy w FF 50 wykazały ciekawe wyniki: nie tylko czasy były prawie 10 razy mniejsze (!), Ale oba testy „forEach” były tak szybkie, jak „pętla zwrotna”. Może faceci z Node powinni używać silnika JS Mozilli zamiast V8? :)
Fr0sT
4

Sposób, w jaki to robisz, nie jest szybszy (poza tym, że jest to pętla na czas nieokreślony, myślę, że chciałeś to zrobić i--.

Jeśli chcesz przyspieszyć, wykonaj następujące czynności:

for (i = 10; i--;) {
    //super fast loop
}

oczywiście nie zauważyłbyś tego w tak małej pętli. Powodem jest szybsze, ponieważ zmniejszasz i sprawdzając, czy jest to „prawda” (zmienia się na „fałsz”, gdy osiągnie 0)

peirix
źródło
1
Brakuje Ci średnika? (i = 10; i--;)
Searlea
Tak tak, naprawiłem błąd. A jeśli będziemy wybredni, wskażę, że zapomniałeś średnika po tym, jak ... Heh
djdd87
Dlaczego miałoby to być szybsze? Krótsze źródło nie oznacza, że ​​będzie szybsze. Zmierzyłeś to?
Greg Hewgill
4
Tak, zmierzyłem to i nie twierdzę, że krótsze źródło przyspiesza, ale mniej operacji przyspiesza.
peirix
4

Czasami wprowadzenie niewielkich zmian w sposobie pisania kodu może mieć duży wpływ na szybkość jego działania. Jednym z obszarów, w którym niewielka zmiana kodu może znacząco wpłynąć na czas wykonania, jest pętla for przetwarzająca tablicę. Tam, gdzie tablica zawiera elementy na stronie internetowej (takie jak przyciski opcji), zmiana ma największy efekt, ale nadal warto zastosować tę zmianę, nawet jeśli tablica jest wewnętrzna w kodzie JavaScript.

Konwencjonalny sposób kodowania pętli for w celu przetworzenia tablicy lis w następujący sposób:

for (var i = 0; i < myArray.length; i++) {...

Problem polega na tym, że ocena długości tablicy za pomocą myArray.length wymaga czasu, a sposób, w jaki zakodowaliśmy pętlę, oznacza, że ​​ta ocena musi być przeprowadzana za każdym razem wokół pętli. Jeśli tablica zawiera 1000 elementów, długość tablicy zostanie oszacowana 1001 razy. Jeśli spojrzeliśmy na przyciski opcji i mielibyśmy myForm.myButtons.length, wówczas ocena zajmie jeszcze więcej czasu, ponieważ odpowiednia grupa przycisków w określonym formularzu musi najpierw zostać zlokalizowana, zanim długość będzie można oszacować za każdym razem wokół pętli.

Oczywiście nie oczekujemy zmiany długości tablicy podczas jej przetwarzania, więc wszystkie te ponowne obliczenia długości tylko niepotrzebnie zwiększają czas przetwarzania. (Oczywiście, jeśli masz kod w pętli, który dodaje lub usuwa wpisy tablicy, rozmiar tablicy może się zmieniać między iteracjami, więc nie możemy zmienić kodu, który ją testuje)

To, co możemy zrobić, aby to poprawić w przypadku pętli, w której rozmiar jest stały, to ocenić długość raz na początku pętli i zapisać ją w zmiennej. Następnie możemy przetestować zmienną, aby zdecydować, kiedy zakończyć pętlę. Jest to o wiele szybsze niż ocenianie długości tablicy za każdym razem, szczególnie gdy tablica zawiera więcej niż kilka wpisów lub jest częścią strony internetowej.

Kod do wykonania tego jest:

for (var i = 0, var j = myArray.length; i < j; i++) {...

Więc teraz oceniamy rozmiar tablicy tylko raz i testujemy licznik pętli na zmiennej, która przechowuje tę wartość za każdym razem wokół pętli. Dostęp do tej dodatkowej zmiennej jest znacznie szybszy niż ocena rozmiaru tablicy, dzięki czemu nasz kod będzie działał znacznie szybciej niż wcześniej. W naszym skrypcie mamy tylko jedną dodatkową zmienną.

Często nie ma znaczenia, w jakiej kolejności przetwarzamy tablicę, dopóki wszystkie wpisy w tablicy zostaną przetworzone. W takim przypadku możemy nieco przyspieszyć nasz kod, usuwając dodatkową zmienną, którą właśnie dodaliśmy, i przetwarzając tablicę w odwrotnej kolejności.

Końcowy kod, który przetwarza naszą tablicę w najbardziej efektywny możliwy sposób, to:

for (var i = myArray.length-1; i > -1; i--) {...

Ten kod wciąż ocenia rozmiar tablicy tylko raz na początku, ale zamiast porównywać licznik pętli ze zmienną, porównujemy ją ze stałą. Ponieważ stała jest jeszcze bardziej efektywna w dostępie niż zmienna, a ponieważ mamy jedną mniej instrukcji przypisania niż wcześniej, nasza trzecia wersja kodu jest teraz nieco bardziej wydajna niż druga wersja i znacznie wydajniejsza niż pierwsza.

Sid
źródło
4

++vs. --nie ma znaczenia, ponieważ JavaScript jest językiem interpretowanym, a nie skompilowanym. Każda instrukcja tłumaczy na więcej niż jeden język maszynowy i nie powinieneś przejmować się krwawymi szczegółami.

Ludzie, którzy mówią o używaniu --(lub ++) do efektywnego korzystania z instrukcji montażu, są w błędzie. Te instrukcje dotyczą arytmetyki liczb całkowitych, aw JavaScript nie ma liczb całkowitych, tylko liczby .

Powinieneś napisać czytelny kod.

Salman A.
źródło
3

W wielu przypadkach nie ma to w zasadzie nic wspólnego z faktem, że procesory mogą porównywać do zera szybciej niż inne porównania.

Wynika to z faktu, że tylko kilka silników Javascript (tych z listy JIT) faktycznie generuje kod języka maszynowego.

Większość silników Javascript tworzy wewnętrzną reprezentację kodu źródłowego, który następnie interpretuje (aby dowiedzieć się, jak to jest, spójrz do dolnej części tej strony na SpiderMonkey w Firefoksie ). Ogólnie rzecz biorąc, jeśli fragment kodu robi praktycznie to samo, ale prowadzi do prostszej wewnętrznej reprezentacji, będzie działał szybciej.

Należy pamiętać, że przy prostych zadaniach, takich jak dodawanie / odejmowanie jednej zmiennej lub porównywanie zmiennej z czymś, narzut tłumacza przechodzący z jednej wewnętrznej „instrukcji” do drugiej jest dość wysoki, więc mniej „instrukcji”, które są używane wewnętrznie przez silnik JS, tym lepiej.

Artelius
źródło
3

Cóż, nie wiem o JavaScript, tak naprawdę powinna to być kwestia ponownej oceny długości tablicy i być może coś wspólnego z tablicami asocjacyjnymi (jeśli tylko zmniejszysz, jest mało prawdopodobne, że nowe wpisy będą musiały zostać przydzielone - jeśli tablica jest gęsta, to znaczy. ktoś może to zoptymalizować).

W asemblerze niskiego poziomu istnieje instrukcja pętli zwana DJNZ (zmniejszanie i przeskakiwanie, jeśli nie jest zero). Tak więc zmniejszenie i skok są w jednej instrukcji, co sprawia, że ​​jest to prawdopodobnie nieco szybsze niż INC i JL / JB (przyrost, skok, jeśli mniej niż / skok, jeśli poniżej). Również porównanie z zerem jest łatwiejsze niż porównanie z inną liczbą. Ale to wszystko jest naprawdę marginalne i zależy również od architektury docelowej (może to mieć znaczenie np. W Uzbrojeniu w smartfonie).

Nie spodziewałbym się, że różnice niskiego poziomu będą miały tak duży wpływ na języki interpretowane, po prostu nie widziałem DJNZ wśród odpowiedzi, więc pomyślałem, że podzielę się ciekawą myślą.

świń
źródło
Dla DJNZprzypomnienia , jest instrukcją w ISA 8051 (z80). x86 ma dec/jnzzamiast tego inc/cmp/jnei najwyraźniej ramię ma coś podobnego. Jestem prawie pewien, że nie jest to przyczyną różnicy w Javascript; to z konieczności ewaluacji więcej w pętli.
Peter Cordes,
3

jest używany do powiedzenia, że --i był szybszy (w C ++), ponieważ jest tylko jeden wynik, wartość zmniejszany. i-- musi zapisać zmniejszoną wartość z powrotem do i, a także zachować oryginalną wartość jako wynik (j = i--;). W większości kompilatorów wykorzystywało to raczej dwa rejestry niż jeden, co może powodować konieczność zapisania innej zmiennej w pamięci, a nie zachowania jej jako zmiennej rejestru.

Zgadzam się z tymi, którzy powiedzieli, że w dzisiejszych czasach nie ma znaczenia.

Mrówka
źródło
3

W bardzo prostych słowach

„i-- i i ++. Właściwie oba zajmują ten sam czas”.

ale w tym przypadku, gdy wykonujesz operację przyrostową .. procesor ocenia długość. za każdym razem, gdy zmienna jest zwiększana o 1, aw przypadku zmniejszenia wartości .. szczególnie w tym przypadku, ocenia długość. tylko raz, aż otrzymamy 0.

Ravi_Parmar
źródło
3

Po pierwsze i++i i--zajmij dokładnie ten sam czas w dowolnym języku programowania, w tym JavaScript.

Poniższy kod zajmuje dużo czasu.

Szybki:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

Powolny:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

Dlatego poniższy kod również zajmuje inny czas.

Szybki:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

Powolny:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

PS Slow działa wolno tylko w kilku językach (silniki JavaScript) ze względu na optymalizację kompilatora. Najlepszym sposobem jest użycie „<” zamiast „<=” (lub „=”) i „--i” zamiast „i--” .

Dmitry
źródło
3

I- lub i ++ nie zużywają dużo czasu. Jeśli zagłębisz się głęboko w architekturę procesora, ++jest to szybsze niż --, ponieważ --operacja uzupełni uzupełnienie 2, ale dzieje się to w sprzęcie, dzięki czemu jest szybka i nie ma większej różnicy między tymi, ++a --także te operacje są brane pod uwagę najmniej czasu zużywanego przez procesor.

Pętli for przebiega tak:

  • Zainicjuj zmienną raz na początku.
  • Sprawdź ograniczenia w drugim argumencie pętli, <, >, <=, itd.
  • Następnie zastosuj pętlę.
  • Zwiększyć pętlę i pętla ponownie rzucić te procesy ponownie.

Więc,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

obliczy długość tablicy tylko raz na początku i nie jest to dużo czasu, ale

for(var i = array.length; i--; ) 

obliczy długość każdej pętli, więc zajmie to dużo czasu.

Omar Freewan
źródło
var i = Things.length - 1; i >= 0; i--też obliczy długość 1 raz.
Dmitry
1
Nie jestem pewien, co oznacza „ --operacja dopełni uzupełnienie 2”, ale zakładam, że oznacza to, że coś zaprzeczy. Nie, nie neguje niczego w żadnej architekturze. Odejmowanie 1 jest tak proste, jak dodawanie 1, po prostu tworzysz obwód, który pożycza, a nie przenosi.
Olathe
1

Najlepszym podejściem do odpowiedzi na tego rodzaju pytanie jest wypróbowanie go. Skonfiguruj pętlę liczącą milion iteracji lub cokolwiek innego i zrób to na dwa sposoby. Czas obu pętli i porównaj wyniki.

Odpowiedź prawdopodobnie zależeć będzie od używanej przeglądarki. Niektóre będą miały inne wyniki niż inne.

Greg Hewgill
źródło
4
To wciąż nie odpowiadałoby na jego pytanie, dlaczego jest szybsze.
Dostałby
3
To prawda. Jednak bez znajomości dokładnej implementacji każdego silnika Javascript w każdej przeglądarce odpowiedź na pytanie „dlaczego” jest prawie niemożliwa. Wiele odpowiedzi tutaj omija niepotwierdzone rekomendacje, takie jak „użyj predekrementacji zamiast postdecrementu” (sztuczka optymalizacji C ++) i „porównaj z zero”, co może być prawdą w językach kompilowanych w natywnym kodzie, ale JavaScript jest dość daleki od samego początku PROCESOR.
Greg Hewgill
4
-1 Całkowicie nie zgadzam się, że najlepszym podejściem jest wypróbowanie tego. Kilka przykładów nie zastępuje wiedzy zbiorowej i taki jest cały sens forum takiego jak ten.
Christophe
1

Uwielbiam to, wiele ocen, ale brak odpowiedzi: D

Po prostu porównanie z zerem jest zawsze najszybszym porównaniem

Tak więc (a == 0) jest w rzeczywistości szybsze w zwracaniu wartości True niż (a == 5)

Jest mały i mało znaczący, a przy 100 milionach wierszy w kolekcji jest mierzalny.

tzn. w pętli możesz powiedzieć, gdzie i <= array.length i zwiększać i

w dolnej pętli możesz powiedzieć, gdzie i> = 0 i zamiast tego zmniejszać i.

Porównanie jest szybsze. Nie „kierunek” pętli.

Robert
źródło
Nie ma odpowiedzi na zadane pytanie, ponieważ silniki Javascript są różne, a odpowiedź zależy od tego, w której przeglądarce mierzysz.
Greg Hewgill
Nie, to zasadniczo akceptowane porównania z zerem są najszybsze. Chociaż twoje stwierdzenia są również poprawne, złota zasada porównywania z zerem jest absolutna.
Robert
4
To prawda, tylko jeśli kompilator zdecyduje się na taką optymalizację, co z pewnością nie jest gwarantowane. Kompilator może wygenerować dokładnie ten sam kod dla (a == 0) i (a == 5), z wyjątkiem wartości stałej. Procesor nie porówna się z 0 szybciej niż z jakąkolwiek inną wartością, jeśli obie strony porównania są zmienne (z punktu widzenia CPU). Zasadniczo tylko natywne kompilatory kodu mają możliwość optymalizacji na tym poziomie.
Greg Hewgill
1

POMÓŻ INNYM UNIKAĆ SŁUCHAWKI --- ZAGŁOSUJ TO!

Najpopularniejsza odpowiedź na tej stronie nie działa w przeglądarce Firefox 14 i nie przechodzi przez jsLinter. Pętle „while” wymagają operatora porównania, a nie przypisania. Działa na Chrome, Safari, a nawet np. Ale umiera w firefoxie.

TO ZŁOŻYŁO SIĘ!

var i = arr.length; //or 10
while(i--)
{
  //...
}

TO DZIAŁA! (działa na Firefoksie, przekazuje jsLinter)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}
mrbinky3000
źródło
2
Właśnie wypróbowałem to na Firefox 14 i działało dobrze. Widziałem przykłady z bibliotek produkcyjnych, które używają while (i--)i które działają w wielu przeglądarkach. Czy w twoim teście mogło być coś dziwnego? Czy używasz wersji beta przeglądarki Firefox 14?
Matt Browne
1

To tylko przypuszczenie, ale może dlatego, że procesorowi łatwiej jest porównać coś z 0 (i> = 0) zamiast z inną wartością (i <Things.length).

Rorchackh
źródło
3
+1 Czy inni nie głosowali. Chociaż powtórna ocena długości jest znacznie większym problemem niż faktyczny przyrost / spadek, sprawdzenie końca pętli może być ważne. Mój kompilator C podaje kilka ostrzeżeń dla pętli, takich jak: „uwaga # 1544-D: (ULP 13.1) Wykryto, że pętla zlicza w górę. Zaleca się, aby pętle odliczały, ponieważ łatwiej jest wykryć zera”
harper