Długa wersja...
Pewien współpracownik zapewnił mnie dzisiaj po obejrzeniu mojego użycia while (1)
w skrypcie Perla, który for (;;)
jest szybszy. Argumentowałem, że powinny być takie same, mając nadzieję, że tłumacz zoptymalizuje wszelkie różnice. Skonfigurowałem skrypt, który będzie uruchamiał 1 000 000 000 dla iteracji pętli i tej samej liczby pętli while i rejestruje czas między nimi. Nie mogłem znaleźć żadnej znaczącej różnicy. Mój współpracownik powiedział, że profesor powiedział mu, że while (1)
robi porównanie, 1 == 1
a for (;;)
nie. Powtórzyliśmy ten sam test przy 100-krotnej liczbie iteracji w C ++ i różnica była nieistotna. Był to jednak graficzny przykład tego, o ile szybciej można skompilować kod w porównaniu z językiem skryptowym.
Krótka wersja...
Czy jest jakiś powód, aby preferować a while (1)
over a, for (;;)
jeśli potrzebujesz nieskończonej pętli, z której chcesz się wyrwać?
Uwaga: jeśli nie wynika to jasno z pytania. To była czysto zabawna dyskusja akademicka między kilkoma przyjaciółmi. Zdaję sobie sprawę, że nie jest to super ważna koncepcja, nad którą wszyscy programiści powinni się męczyć. Dzięki za wszystkie świetne odpowiedzi. Ja (i jestem pewien, że inni) nauczyłem się kilku rzeczy z tej dyskusji.
Aktualizacja: Wspomniany wyżej współpracownik odniósł się do odpowiedzi poniżej.
Cytowany tutaj na wypadek, gdyby został zakopany.
Pochodzi od programisty zespołu AMD. Stwierdził, że programiści C (ludzie) nie zdają sobie sprawy, że ich kod jest nieefektywny. Powiedział jednak dzisiaj, że kompilatory gcc są bardzo dobre i pozbawiają ludzi takich jak on interes. Powiedział na przykład, i powiedział mi o
while 1
vsfor(;;)
. Używam go teraz z przyzwyczajenia, ale gcc, a zwłaszcza interpretery, będą wykonywać tę samą operację (skok procesora) przez oba te dni, ponieważ są zoptymalizowane.
źródło
Odpowiedzi:
W perlu dają te same rozkazy:
Podobnie w GCC:
Więc myślę, że odpowiedź brzmi: są takie same w wielu kompilatorach. Oczywiście w przypadku niektórych innych kompilatorów niekoniecznie tak musi być, ale jest szansa, że kod wewnątrz pętli i tak będzie kilka tysięcy razy droższy niż sama pętla, więc kogo to obchodzi?
źródło
Używając GCC, oba wydają się kompilować do tego samego języka asemblera:
źródło
Nie ma powodu, aby preferować jedno nad drugim. I sądzę, że
while(1)
w szczególnościwhile(true)
są bardziej czytelne niżfor(;;)
, ale to tylko moje preferencje.źródło
forever
jest to jego własny token.Nie ma różnicy w stosunku do normy. 6.5.3 / 1 ma:
A 6.5.3 / 2 ma:
Czyli zgodnie ze standardem C ++ kod:
jest dokładnie taki sam jak:
źródło
Kompilator Visual C ++ używany do emitowania ostrzeżenia dla platformy
(wyrażenie stałe), ale nie dla
Z
for (;;)
tego powodu kontynuowałem praktykę preferowania , ale nie wiem, czy kompilator nadal to robi.źródło
for(;;)
jest o jeden znak mniej do wpisania, jeśli chcesz iść w tym kierunku, aby zoptymalizować rzeczy.źródło
Turbo C z tymi starymi kompilatorami
for(;;)
skutkuje szybszym kodemwhile(1)
.Dzisiaj kompilatory gcc, Visual C (chyba prawie wszystkie) optymalizują się dobrze, a procesory o częstotliwości 4,7 MHz są rzadko używane.
W tamtych czasach a
for( i=10; i; i-- )
było szybsze niżfor( i=1; i <=10; i++ )
, ponieważ porównaniei
wynosi 0, powoduje warunkowy skok CPU-Zero-Flag. Flaga zera została zmodyfikowana podczas ostatniej operacji dekrementacji( i-- )
, nie jest potrzebna żadna dodatkowa operacja cmp.a tutaj
for(i=1; i<=10; i++)
z dodatkowym cmpl:źródło
Dla wszystkich ludzi, którzy argumentują, że nie powinieneś używać indefinte while i sugerują głupie rzeczy, takie jak używanie otwartego goto (serio, ouch)
Tak naprawdę nie można go skutecznie przedstawić w żaden inny sposób. Nie bez utworzenia zmiennej wyjściowej i zrobienia czarnej magii, aby ją zsynchronizować.
Jeśli masz skłonność do składni bardziej goto-esque, użyj czegoś rozsądnego, co ogranicza zakres.
Ostatecznie szybkość nie jest taka ważna
Martwienie się o to, jak efektywne pod względem szybkości są różne konstrukcje pętli, jest ogromną stratą czasu. Przedwczesna optymalizacja na wskroś. Nie przychodzi mi do głowy żadna sytuacja, jaką kiedykolwiek widziałem, w której kod profilujący znalazłby wąskie gardła w moim wyborze konstrukcji pętli.
Ogólnie jest to jak pętla i co z pętli.
Powinieneś "zoptymalizować" pod kątem czytelności i zwięzłości oraz napisać wszystko, co najlepiej wyjaśni problem następnemu biednemu frajerowi, który znajdzie twój kod.
Jeśli używasz sztuczki „goto LABEL”, o której ktoś wspomniał, a ja muszę użyć twojego kodu, przygotuj się na spanie z jednym okiem otwartym, zwłaszcza jeśli robisz to więcej niż raz, ponieważ takie rzeczy tworzą okropny kod spaghetti.
Tylko dlatego, że można tworzyć spaghetti code nie znaczy, że powinniśmy
źródło
Stroustrup, TC ++ PL (wydanie trzecie), §6.1.1:
Wolę
for (;;)
.źródło
Jeśli kompilator nie przeprowadza żadnej optymalizacji,
for(;;)
zawsze będzie szybszy niżwhile(true)
. Dzieje się tak, ponieważ instrukcja while oblicza warunek za każdym razem, ale instrukcja for jest bezwarunkowym skokiem. Ale jeśli kompilator zoptymalizuje przepływ sterowania, może wygenerować niektóre rozkazy. Możesz bardzo łatwo odczytać kod demontażu.PS możesz napisać nieskończoną pętlę w ten sposób:
źródło
Słyszałem o tym kiedyś.
Pochodzi od programisty zespołu AMD. Stwierdził, że programiści C (ludzie) nie zdają sobie sprawy, że ich kod jest nieefektywny. Powiedział jednak dzisiaj, że kompilatory gcc są bardzo dobre i pozbawiają ludzi takich jak on interes. Powiedział na przykład, i powiedział mi o
while 1
vsfor(;;)
. Używam go teraz z przyzwyczajenia, ale gcc, a zwłaszcza interpretery, będą wykonywać tę samą operację (skok procesora) przez oba te dni, ponieważ są zoptymalizowane.źródło
W zoptymalizowanej wersji skompilowanego języka nie powinno być między nimi zauważalnej różnicy. Żadne z nich nie powinno kończyć się przeprowadzaniem jakichkolwiek porównań w czasie wykonywania, po prostu wykonają kod pętli, dopóki ręcznie nie wyjdziesz z pętli (np. Za pomocą a
break
).źródło
Dziwię się, że nikt właściwie przetestowane
for (;;)
kontrawhile (1)
Perl!Ponieważ perl jest językiem interpretowanym, czas potrzebny na uruchomienie skryptu w Perlu składa się nie tylko z fazy wykonania (która w tym przypadku jest taka sama), ale także z fazy interpretacji przed wykonaniem. Przy porównywaniu prędkości należy wziąć pod uwagę obie te fazy.
Na szczęście perl ma wygodny moduł Benchmark, za pomocą którego możemy zaimplementować benchmark, taki jak:
Zauważ, że testuję dwie różne wersje nieskończonej pętli for: jedną, która jest krótsza niż pętla while, i drugą, która ma dodatkową przestrzeń, aby uzyskać taką samą długość jak pętla while.
Na Ubuntu 11.04 x86_64 z perlem 5.10.1 otrzymuję następujące wyniki:
Pętla while jest zdecydowanie zwycięzcą na tej platformie.
We FreeBSD 8.2 x86_64 z perlem 5.14.1:
Podczas gdy pętla jest tutaj również zwycięzcą.
W FreeBSD 8.2 i386 z perl 5.14.1:
Zaskakujące jest, że pętla for z dodatkową przestrzenią jest tutaj najszybszym wyborem!
Mój wniosek jest taki, że pętla while powinna być używana na platformie x86_64, jeśli programista optymalizuje pod kątem szybkości. Oczywiście pętla for powinna być używana podczas optymalizacji przestrzeni. Moje wyniki są niestety niejednoznaczne w odniesieniu do innych platform.
źródło
Benchmark
ma swoje ograniczenia i nie można go użyć do odróżnienia szybkiego od wolnego, jeśli wyniki mieszczą się w granicach 7%. Co więcej, nie przetestowałeś różnicy między pętlamifor
i,while
ponieważ każdy subwoofer będzie działałdie
przed osiągnięciem samych pętli. I od kiedy ilość białych znaków ma znaczenie dla interpretera Perla? Przepraszamy, ale analiza jest bardzo błędna.die
jest w moim kodzie, ponieważ moim zamiarem jest przetestowanie tylko różnicy czasu kompilacji . Jak inni już zauważyli, wynikowy kod bajtowy jest identyczny, więc nie ma sensu go testować. Zaskakująco ilość białej przestrzeni wydaje się mieć niewielką różnicę w tym przypadku w moich środowiskach testowych. Może to mieć coś wspólnego z tym, jak postacie zostaną wyrównane w pamięci lub coś podobnego ...Benchmark
które nie są rozstrzygające. W ogóle nie ufaj tej 1% różnicy!-60
uruchamia test przez 60 sekund.Teoretycznie całkowicie naiwny kompilator mógłby przechowywać dosłowne „1” w pliku binarnym (marnowanie miejsca) i sprawdzać, czy 1 == 0 w każdej iteracji (marnowanie czasu i więcej miejsca).
Jednak w rzeczywistości, nawet przy braku optymalizacji, kompilatory nadal będą redukować oba do tego samego. Mogą również emitować ostrzeżenia, ponieważ może to wskazywać na błąd logiczny. Na przykład argument
while
można zdefiniować gdzie indziej i nie zdajesz sobie sprawy, że jest stały.źródło
Dziwię się, że nikt nie zaproponował bardziej bezpośredniej formy, odpowiadającej żądanemu montażowi:
źródło
{ forever: do stuff; goto forever; }
)while(1)
to idiomfor(;;)
rozpoznawany przez większość kompilatorów.Cieszyłem się, że Perl też to rozpoznaje
until(0)
.źródło
Podsumowując debatę na temat
for (;;)
vswhile (1)
, jest oczywiste, że ta pierwsza była szybsza w czasach starszych nieoptymalizujących kompilatorów, dlatego zwykle widzisz ją w starszych bazach kodu, takich jak komentarze do kodu źródłowego Lions Unix, jednak w dobie optymalizacji kompilatory te zyski są zoptymalizowane, łącząc to z faktem, że to drugie jest łatwiejsze do zrozumienia niż pierwsze, uważam, że byłoby bardziej korzystne.źródło
Właśnie natknąłem się na ten wątek (chociaż spóźniony o kilka lat).
Myślę, że znalazłem właściwy powód, dla którego „za (;;)” jest lepsze niż „podczas (1)”.
zgodnie ze „standardem kodowania barier 2018”
w zasadzie nie jest to problem z szybkością, ale z czytelnością. W zależności od czcionki / nadruku kodu numer jeden (1) może wyglądać jak mała litera l.
czyli 1 vs l. (w niektórych czcionkach wyglądają identycznie).
Więc while (1) może wyglądać jak pętla while zależna od litery zmiennej L.
while (true) może również działać, ale w niektórych starszych przypadkach C i embedded C true / false nie są jeszcze zdefiniowane, chyba że dołączono stdbool.h.
źródło
l
, a nie to1
il
może wyglądać podobnie.Myślę, że oba są takie same pod względem wydajności. Ale wolałbym while (1) ze względu na czytelność, ale pytam, dlaczego potrzebujesz nieskończonej pętli.
źródło
Oni są tacy sami. Jest dużo ważniejszych pytań do rozważenia.
Mój punkt widzenia, który został dorozumiany, ale nie został wyraźnie przedstawiony powyżej, jest taki, że przyzwoity kompilator wygenerowałby dokładnie ten sam kod dla obu form pętli. Większe jest to, że konstrukcja pętli jest mniejszą częścią czasu wykonywania dowolnego algorytmu i musisz najpierw upewnić się, że zoptymalizowałeś algorytm i wszystko inne z nim związane. Optymalizacja konstrukcji pętli absolutnie powinna znajdować się na dole listy priorytetów.
źródło