W C # / VB.NET / .NET, która pętla działa szybciej for
lub foreach
?
Odkąd przeczytałem, że już dawnofor
pętla działa szybciej niż foreach
pętla , zakładałem, że jest prawdziwa dla wszystkich kolekcji, zbiorów ogólnych, wszystkich tablic itp.
Przeszukałem Google i znalazłem kilka artykułów, ale większość z nich jest niejednoznaczna (czytaj komentarze do artykułów) i otwarta.
Idealnym rozwiązaniem byłoby wyszczególnienie każdego scenariusza i najlepsze rozwiązanie tego samego.
Na przykład (tylko przykład tego, jak powinno być):
- do iteracji tablicy ponad 1000 ciągów -
for
jest lepszy niżforeach
- do iteracji po
IList
(nie rodzajowych) ciągach -foreach
jest lepszy niżfor
Kilka odnośników znalezionych w sieci dla tego samego:
- Oryginalny wielki stary artykuł Emmanuela Schanzera
- CodeProject FOREACH vs. DLA
- Blog - To
foreach
lub nieforeach
, to jest pytanie - Forum ASP.NET - NET 1.1 C #
for
vsforeach
[Edytować]
Oprócz aspektu czytelności, naprawdę interesują mnie fakty i liczby. Istnieją aplikacje, w których ostatnia mila optymalizacji wydajności ma znaczenie.
c#
.net
performance
for-loop
Binoj Antony
źródło
źródło
foreach
zamiastfor
w języku C #. Jeśli widzisz tutaj odpowiedzi, które w ogóle nie mają sensu, właśnie dlatego. Obwiniaj moderatora, a nie nieszczęśliwe odpowiedzi.Odpowiedzi:
Patrick Smacchia blogował o tym w zeszłym miesiącu, z następującymi wnioskami:
źródło
foreach
niż zapętlenie tablicyfor
, a ty nazywasz to nieistotnym? Tego rodzaju różnica w wydajności może mieć znaczenie dla twojej aplikacji i może nie, ale nie po prostu odrzuciłbym to z ręki.foreach
s.Po pierwsze, roszczenie wzajemne do odpowiedzi Dmitry'ego (obecnie usunięte) . W przypadku tablic kompilator C # emituje w dużej mierze ten sam kod,
foreach
jak w przypadku równoważnejfor
pętli. To wyjaśnia, dlaczego dla tego testu porównawczego wyniki są w zasadzie takie same:Wyniki:
Następnie, sprawdzenie, czy Greg podkreśla, że typ kolekcji jest ważny - zmień tablicę na a
List<double>
powyżej, a otrzymasz radykalnie różne wyniki. Jest nie tylko ogólnie wolniejszy, ale foreach staje się znacznie wolniejszy niż dostęp przez indeks. Powiedziawszy to, nadal prawie zawsze wolę foreach od pętli for, co upraszcza kod - ponieważ czytelność jest prawie zawsze ważna, podczas gdy mikrooptymalizacja rzadko.źródło
List<T>
? Czy w tym przypadku również czytelność przebija mikrooptymalizację?List<T>
tablice. Wyjątkami sąchar[]
ibyte[]
które są częściej traktowane jako „fragmenty” danych niż zwykłe kolekcje.foreach
Pętle wykazują bardziej konkretne zamiary niżfor
pętle .Użycie
foreach
pętli pokazuje każdemu, kto używa twojego kodu, że planujesz zrobić coś z każdym członkiem kolekcji, niezależnie od jej miejsca w kolekcji. Pokazuje również, że nie modyfikujesz oryginalnej kolekcji (i zgłasza wyjątek, jeśli spróbujesz).Inną zaletą
foreach
jest to, że działa na każdymIEnumerable
, gdziefor
ma to sensIList
, gdzie każdy element faktycznie ma indeks.Jeśli jednak potrzebujesz użyć indeksu elementu, to oczywiście powinieneś mieć możliwość korzystania z
for
pętli. Ale jeśli nie musisz używać indeksu, posiadanie go jest po prostu zaśmiecaniem kodu.O ile mi wiadomo, nie ma to znaczącego wpływu na wydajność. Na pewnym etapie w przyszłości może być łatwiej dostosować kod,
foreach
aby działał na wielu rdzeniach, ale teraz nie jest to coś, o co należy się martwić.źródło
foreach
.foreach
wcale nie jest związany z programowaniem funkcjonalnym. Jest to absolutnie konieczny paradygmat programowania. Źle przypisujesz rzeczy, które wydarzyły się w TPL i PLINQforeach
.foreach
jako odpowiednikwhile
pętli). Myślę, że wiem, że @ctford ma na myśli. Równoległa biblioteka zadań pozwala podstawowej kolekcji na dostarczanie elementów w dowolnej kolejności (przez wywołanie.AsParallel
wyliczenia).foreach
nic tu nie robi, a ciało pętli jest wykonywane na jednym wątku . Jedyną równoległą rzeczą jest generowanie sekwencji.foreach
afor
wydajnością wynosi ułamki sekundy w przypadku iteracji ponad milionów elementów, więc Twój problem z pewnością nie był bezpośrednio związany z wydajnością foreach, przynajmniej nie dla kilkuset obiektów. Brzmi jak zepsuta implementacja modułu wyliczającego na dowolnej używanej liście.Za każdym razem, gdy pojawiają się spory dotyczące wydajności, wystarczy napisać mały test, aby można było wykorzystać wyniki ilościowe do wsparcia swojego przypadku.
Użyj klasy StopWatch i powtórz coś kilka milionów razy, aby uzyskać dokładność. (Może to być trudne bez pętli for):
Palce przekroczyły wyniki tego pokazu, że różnica jest znikoma i równie dobrze możesz po prostu zrobić wszystko, co w najbardziej konserwowalnym kodzie
źródło
Zawsze będzie blisko. W przypadku tablicy czasami
for
jest ona nieco szybsza, aleforeach
jest bardziej wyrazista i oferuje LINQ itp. Ogólnie rzecz biorąc, trzymaj sięforeach
.Dodatkowo
foreach
może być zoptymalizowany w niektórych scenariuszach. Na przykład lista połączona może być okropna dla indeksatora, ale może być szybkaforeach
. W rzeczywistości standardLinkedList<T>
nie oferuje nawet indeksatora z tego powodu.źródło
LinkedList<T>
jest szczuplejszy niżList<T>
? A jeśli zawsze będę używaćforeach
(zamiastfor
), lepiej będzie używaćLinkedList<T>
?List<T>
). Chodzi o to, że tańsze jest wkładanie / wyjmowanie .Domyślam się, że w 99% przypadków prawdopodobnie nie będzie to znaczące, więc dlaczego miałbyś wybrać szybszy zamiast najbardziej odpowiedniego (jak najłatwiej zrozumieć / utrzymać)?
źródło
Jest mało prawdopodobne, aby istniała ogromna różnica wydajności między tymi dwoma. Jak zawsze w obliczu „który jest szybszy?” pytanie, zawsze powinieneś pomyśleć: „Mogę to zmierzyć”.
Napisz dwie pętle, które robią to samo w ciele pętli, wykonaj je i zrób z nich czas i zobacz, jaka jest różnica prędkości. Zrób to zarówno z prawie pustym ciałem, jak i ciałem pętli podobnym do tego, co faktycznie będziesz robić. Wypróbuj również z używanym typem kolekcji, ponieważ różne typy kolekcji mogą mieć różne cechy wydajności.
źródło
Istnieją bardzo dobre powody, aby preferować
foreach
pętlefor
zamiast pętli. Jeśli możesz użyćforeach
pętli, twój szef ma rację, że powinieneś.Jednak nie każda iteracja po prostu przegląda listę w kolejności jeden po drugim. Jeśli zabrania , tak, to źle.
Gdybym był tobą, zrobiłbym wszystko, aby wszystkie naturalne pętle pętli przekształciły się w rekurencję . To by go nauczyło i jest to również dobre ćwiczenie mentalne dla ciebie.
źródło
for
pętli iforeach
pętle wydajność mądry?Jeffrey Richter na TechEd 2005:
Transmisja internetowa na żądanie: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US
źródło
To jest niedorzeczne. Nie ma ważnego powodu, aby zakazać pętli for, pod względem wydajności lub innej.
Zobacz blog Jona Skeeta, w którym znajduje się test wydajności i inne argumenty.
źródło
W przypadkach, gdy pracujesz z kolekcją obiektów,
foreach
lepiej, ale jeśli zwiększysz liczbę, afor
pętla jest lepsza.Pamiętaj, że w ostatnim przypadku możesz zrobić coś takiego:
Ale z pewnością nie działa lepiej, w rzeczywistości ma gorszą wydajność w porównaniu do
for
.źródło
To powinno cię uratować:
Posługiwać się:
Aby uzyskać większą wygraną, możesz wziąć trzech delegatów jako parametry.
źródło
for
zwykle zapisywana jest pętla wykluczająca koniec zakresu (np0 <= i < 10
.).Parallel.For
robi to również, aby łatwo było go zamienić na wspólnąfor
pętlę.możesz przeczytać o tym w Deep .NET - część 1 Iteracja
obejmuje wyniki (bez pierwszej inicjalizacji) z kodu źródłowego .NET aż do dezasemblacji.
na przykład - Iteracja macierzy za pomocą pętli foreach:
oraz - wypisz iterację z pętlą foreach:
a wyniki końcowe:
źródło
Różnice prędkości w pętli
for
- i a -foreach
są niewielkie, gdy przeglądasz typowe struktury, takie jak tablice, listy itp., I robiszLINQ
wyszukiwanie w kolekcji jest prawie zawsze nieco wolniejsze, chociaż ładniej jest pisać! Jak powiedzieli inni plakaty, wybieraj wyrazistość zamiast milisekundy dodatkowej wydajności.Do tej pory nie powiedziano, że
foreach
kompilacja pętli jest optymalizowana przez kompilator na podstawie kolekcji, nad którą się iteruje. Oznacza to, że jeśli nie masz pewności, której pętli użyć, powinieneś użyćforeach
pętli - wygeneruje ona najlepszą pętlę po skompilowaniu. Jest też bardziej czytelny.Inną kluczową zaletą
foreach
pętli jest to, że jeśli implementacja kolekcji zmieni się ( na przykład z intarray
na intList<int>
), wówczasforeach
pętla nie będzie wymagać żadnych zmian kodu:Powyższe jest takie samo, bez względu na typ kolekcji, podczas gdy w
for
pętli następujące elementy nie będą budowane, jeśli zmieniszmyCollection
z aarray
naList
:źródło
„Czy są jakieś argumenty, które mogłyby mi pomóc przekonać go, że można użyć pętli for?”
Nie, jeśli twój szef zarządza na tyle, że mówi, z jakiego języka programowania należy korzystać, naprawdę nie możesz nic powiedzieć. Przepraszam.
źródło
Prawdopodobnie zależy to od rodzaju wyliczanej kolekcji i implementacji jej modułu indeksującego. Ogólnie rzecz biorąc, używanie
foreach
może być lepszym podejściem.Będzie także działał z dowolnymi
IEnumerable
- nie tylko z indeksatorami.źródło
To ma te same dwie odpowiedzi, co większość pytań „co jest szybsze”:
1) Jeśli nie mierzysz, nie wiesz.
2) (Ponieważ ...) To zależy.
Zależy to od tego, jak droga jest metoda „MoveNext ()”, w stosunku do tego, jak droga jest metoda „this [int index]”, dla typu (lub typów) IEnumerable, nad którym będziesz iterować.
Słowo kluczowe „foreach” jest skrótem dla szeregu operacji - wywołuje GetEnumerator () jeden raz w IEnumerable, wywołuje MoveNext () raz na iterację, wykonuje sprawdzanie typów i tak dalej. Najbardziej prawdopodobne, że wpływ na pomiary wydajności będzie miał koszt MoveNext (), ponieważ jest on wywoływany O (N) razy. Może to tanie, ale może nie.
Słowo kluczowe „for” wygląda bardziej przewidywalnie, ale w większości pętli „for” znajdziesz coś w rodzaju „kolekcja [indeks]”. Wygląda to na prostą operację indeksowania tablic, ale w rzeczywistości jest to wywołanie metody, której koszt zależy wyłącznie od charakteru kolekcji, nad którą się iteruje. Prawdopodobnie jest tani, ale może nie jest.
Jeśli podstawowa struktura kolekcji jest w zasadzie połączoną listą, MoveNext jest tani, ale indeksator może mieć koszt O (N), co czyni prawdziwy koszt pętli „for” O (N * N).
źródło
Każdy konstrukt językowy ma odpowiedni czas i miejsce do użycia. Istnieje powód, dla którego język C # ma cztery oddzielne instrukcje iteracji - każda z nich służy do określonego celu i ma odpowiednie zastosowanie.
Polecam usiąść z szefem i racjonalnie wyjaśnić, dlaczego
for
pętla ma cel. Są chwile, kiedyfor
blok iteracji bardziej precyzyjnie opisuje algorytm niżforeach
iteracja. Jeśli jest to prawdą, należy z nich skorzystać.Chciałbym również zwrócić uwagę na twojego szefa - Wydajność nie jest i nie powinna stanowić problemu w żaden praktyczny sposób - jest to bardziej kwestia wyrażenia algorytmu w zwięzły, znaczący i łatwy do utrzymania sposób. Mikrooptymalizacje takie jak ten całkowicie pomijają sens optymalizacji wydajności, ponieważ jakakolwiek rzeczywista korzyść wydajności będzie wynikać z przeprojektowania algorytmu i refaktoryzacji, a nie restrukturyzacji pętli.
Jeśli po racjonalnej dyskusji nadal istnieje autorytarny pogląd, od Ciebie zależy, jak postępować. Osobiście nie byłbym szczęśliwy pracując w środowisku, w którym racjonalne myślenie jest odradzane, i rozważałbym przejście na inne stanowisko pod innym pracodawcą. Jednak zdecydowanie zalecam dyskusję przed denerwowaniem - może to być zwykłe nieporozumienie.
źródło
To, co robisz wewnątrz pętli, ma wpływ na wydajność, a nie faktyczna konstrukcja pętli (zakładając, że twój przypadek nie jest trywialny).
źródło
To, czy
for
jest szybsze, niżforeach
jest naprawdę poza tym. Poważnie wątpię, że wybranie jednego z drugiego będzie miało znaczący wpływ na twoje wyniki.Najlepszym sposobem na zoptymalizowanie aplikacji jest profilowanie rzeczywistego kodu. To wskaże metody, które odpowiadają za najwięcej pracy / czasu. Najpierw zoptymalizuj. Jeśli wydajność nadal nie jest akceptowalna, powtórz procedurę.
Zasadniczo zalecałbym unikanie mikrooptymalizacji, ponieważ rzadko przynoszą one znaczące korzyści. Jedynym wyjątkiem jest optymalizacja zidentyfikowanych gorących ścieżek (tj. Jeśli twoje profilowanie identyfikuje kilka najczęściej używanych metod, sensowna może być ich szeroka optymalizacja).
źródło
for
jest nieznacznie szybszy niżforeach
. Poważnie sprzeciwiłem się temu stwierdzeniu. To całkowicie zależy od podstawowej kolekcji. Jeśli połączona klasa listy zapewnia indeksatorowi parametr całkowitoliczbowy, oczekiwałbym, żefor
użyje na nim pętli O (n ^ 2), podczas gdyforeach
oczekuje się, że O (n).for
i przeglądarkę indeksującą do używaniaforeach
samego. Myślę, że odpowiedź @Briana Rasmussena jest poprawna, że oprócz użycia z kolekcją,for
zawsze będzie ona nieco szybsza niżforeach
. Jednakfor
wyszukiwanie kolekcji zawsze będzie wolniejsze niżforeach
samo w sobie.for
instrukcji. Zwykłafor
pętla ze zmienną sterującą liczbą całkowitą nie jest porównywalna zforeach
, więc to jest koniec. Rozumiem, co oznacza @Brian i jest to poprawne, jak mówisz, ale odpowiedź może być myląca. Re: twój ostatni punkt: nie, właściwie tofor
koniecList<T>
jest jeszcze szybszy niżforeach
.Oba będą działać prawie dokładnie w ten sam sposób. Napisz kod, aby użyć obu, a następnie pokaż mu IL. Powinien pokazywać porównywalne obliczenia, co oznacza brak różnicy w wydajności.
źródło
for ma prostszą logikę do wdrożenia, dzięki czemu jest szybszy niż foreach.
źródło
Chyba że jesteś w konkretnym procesie optymalizacji prędkości, powiedziałbym, że użycie dowolnej metody zapewni najłatwiejszy do odczytania i utrzymania kod.
Jeśli iterator jest już skonfigurowany, tak jak w przypadku jednej z klas kolekcji, foreach jest dobrą, łatwą opcją. A jeśli iterujesz zakres liczb całkowitych, to prawdopodobnie jest czystszy.
źródło
Jeffrey Richter mówił o różnicy w wydajności między ostatnim i podanym na ostatnim podcastu: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317
źródło
W większości przypadków tak naprawdę nie ma różnicy.
Zazwyczaj zawsze musisz używać foreach, gdy nie masz wyraźnego indeksu liczbowego, i zawsze musisz go używać, gdy nie masz w rzeczywistości kolekcji iteracyjnej (np. Iteracja po dwuwymiarowej siatce tablic w górnym trójkącie) . W niektórych przypadkach masz wybór.
Można argumentować, że pętle mogą być nieco trudniejsze do utrzymania, jeśli w kodzie zaczną pojawiać się magiczne liczby. Powinieneś mieć rację, że nie możesz użyć pętli for i musisz zbudować kolekcję lub użyć lambdy do zbudowania podkolekcji zamiast tego, ponieważ pętle for zostały zablokowane.
źródło
Całkiem dziwne wydaje się całkowite zakazanie używania czegoś w rodzaju pętli for.
Jest tu ciekawy artykuł , który opisuje wiele różnic wydajności między dwiema pętlami.
Powiedziałbym, że osobiście uważam, że foreach jest nieco bardziej czytelny dla pętli, ale powinieneś użyć najlepszego dla danego zadania i nie musisz pisać dodatkowego długiego kodu, aby uwzględnić pętlę foreach, jeśli pętla for jest bardziej odpowiednia.
źródło
Znalazłem
foreach
pętlę, która iterujeList
szybciej . Zobacz moje wyniki testu poniżej. W poniższym kodziearray
iteruję osobno rozmiary 100, 10000 i 100000, używającfor
iforeach
zapętlając do pomiaru czasu.AKTUALIZACJA
Po sugestii @jgauffin użyłem kodu @johnskeet i stwierdziłem, że
for
pętla zarray
jest szybsza niż śledzenie,Zobacz moje wyniki testu i kod poniżej,
źródło
Możesz naprawdę przykręcić mu głowę i zamiast tego wybrać IQueryable .foreach zamknięcie:
źródło
myList.ForEach(Console.WriteLine)
.Nie spodziewałbym się, że ktokolwiek znajdzie „ogromną” różnicę wydajności między tymi dwoma.
Wydaje mi się, że odpowiedź zależy od tego, czy kolekcja, do której próbujesz uzyskać dostęp, ma szybszą implementację dostępu do indeksatora, czy szybszą implementację dostępu do IEnumerator. Ponieważ IEnumerator często używa indeksatora i po prostu przechowuje kopię bieżącej pozycji indeksu, oczekiwałbym, że dostęp do enumeratora będzie co najmniej tak wolny lub wolniejszy niż bezpośredni dostęp do indeksu, ale niewiele.
Oczywiście ta odpowiedź nie uwzględnia żadnych optymalizacji, które kompilator może wdrożyć.
źródło
Należy pamiętać, że pętla for i pętla foreach nie zawsze są równoważne. Moduł wyliczający listy wyrzuci wyjątek, jeśli lista się zmieni, ale nie zawsze otrzymasz to ostrzeżenie z normalną pętlą for. Możesz nawet uzyskać inny wyjątek, jeśli lista zmieni się w niewłaściwym czasie.
źródło