Powiedzmy, że musisz mieć listę / tablicę liczb całkowitych, które potrzebujesz iterować często, a mam na myśli bardzo często. Przyczyny mogą być różne, ale powiedzmy, że jest to sedno najbardziej wewnętrznej pętli przetwarzania o dużej objętości.
Ogólnie rzecz biorąc, można by zdecydować się na korzystanie z list (list) ze względu na ich elastyczność wielkości. Co więcej, dokumentacja msdn twierdzi, że listy używają tablicy wewnętrznie i powinny działać równie szybko (szybkie spojrzenie z Reflector potwierdza to). Jednak zawsze wiąże się to z pewnymi kosztami ogólnymi.
Czy ktoś faktycznie to zmierzył? czy iteracja 6 milionów razy przez listę zajęłaby tyle samo czasu co tablica?
T[]
vs.List<T>
może mieć duży wpływ na wydajność. Właśnie zoptymalizowałem bardzo (zagnieżdżoną) aplikację intensywnie korzystającą z pętli, aby przejść z list do tablic w .NET 4.0. Spodziewałem się poprawy o 5–10%, ale przyspieszyłem o ponad 40%! Żadnych innych zmian niż przejście bezpośrednio z listy do tablicy. Wszystkie wyliczenia zostały wykonane za pomocąforeach
instrukcji. Na podstawie odpowiedzi Marca Gravella wygląda na to, żeforeach
zList<T>
jest szczególnie źle.Odpowiedzi:
Bardzo łatwy do zmierzenia ...
W niewielkiej liczbie kodu przetwarzania o wąskiej pętli, w którym wiem, że długość jest ustalona , używam tablic do tego bardzo małego mikrooptymalizacji; tablice mogą być nieznacznie szybsze, jeśli użyjesz indeksu / dla formularza - ale IIRC wierzy, że zależy to od rodzaju danych w tablicy. Ale chyba, że potrzebujesz mikrooptymalizować, zachowaj prostotę i używaj
List<T>
itp.Oczywiście dotyczy to tylko odczytu wszystkich danych; słownik byłby szybszy dla wyszukiwań opartych na kluczach.
Oto moje wyniki za pomocą „int” (druga liczba to suma kontrolna, aby sprawdzić, czy wszystkie wykonały tę samą pracę):
(edytowany, aby naprawić błąd)
na podstawie zestawu testowego:
źródło
Podsumowanie:
Tablica musi używać:
Lista musi użyć:
LinkedList musi użyć:
W razie potrzeby dodaj komórki na początku / środku / końcu listy (często)
W razie potrzeby tylko dostęp sekwencyjny (do przodu / do tyłu)
Jeśli chcesz zapisać DUŻE przedmioty, ale liczba przedmiotów jest niska.
Lepiej nie używaj do dużej ilości przedmiotów, ponieważ używa to dodatkowej pamięci na linki.
Więcej szczegółów:
Znacznie więcej szczegółów:
https://stackoverflow.com/a/29263914/4423545
źródło
Myślę, że wydajność będzie podobna. Narzutem związanym z korzystaniem z listy w porównaniu z tablicą jest IMHO podczas dodawania elementów do listy i gdy lista musi zwiększyć rozmiar tablicy, której używa wewnętrznie, gdy pojemność tablicy zostanie osiągnięta.
Załóżmy, że masz listę o pojemności 10, a następnie lista zwiększy jej pojemność, gdy będziesz chciał dodać 11 element. Możesz zmniejszyć wpływ na wydajność, inicjując pojemność listy do liczby przechowywanych elementów.
Ale, aby dowiedzieć się, czy iteracja po liście jest tak szybka, jak iteracja po tablicy, dlaczego nie przetestujesz jej?
W moim systemie; iteracja po tablicy zajęła 33 ms; iteracja po liście zajęła 66 ms.
Szczerze mówiąc, nie spodziewałem się, że ta odmiana będzie tak duża. Tak więc umieściłem iterację w pętli: teraz wykonuję obie iteracje 1000 razy. Wyniki są następujące:
Ta odmiana nie jest już tak duża, ale nadal ...
Dlatego uruchomiłem .NET Reflector i moduł pobierania indeksu klasy List wygląda następująco:
Jak widać, podczas korzystania z indeksatora Listy, Lista sprawdza, czy nie wychodzisz poza granice wewnętrznej tablicy. Ta dodatkowa kontrola wiąże się z opłatą.
źródło
jeśli pobierasz tylko jedną wartość z jednego z nich (nie w pętli), oba sprawdzają granice (pamiętasz kod zarządzany), tylko lista robi to dwa razy. Zobacz uwagi później, dlaczego prawdopodobnie nie jest to wielka sprawa.
Jeśli używasz własnego dla (int int i = 0; i <x. [Length / Count]; i ++), kluczowa różnica jest następująca:
Jeśli używasz foreach, kluczowa różnica jest następująca:
Sprawdzanie granic często nie jest wielkim problemem (szczególnie jeśli korzystasz z procesora z głęboką prognozą potoku i rozgałęzienia - normą na większość dni), ale tylko twoje własne profilowanie może ci powiedzieć, czy to problem. Jeśli znajdujesz się w części kodu, w której unikasz alokacji sterty (dobrym przykładem są biblioteki lub implementacje kodu mieszającego), to upewnienie się, że zmienna jest wpisana jako Lista nie IList, pozwoli uniknąć pułapki. Jak zawsze profil, jeśli ma to znaczenie.
źródło
[ Zobacz także to pytanie ]
Zmodyfikowałem odpowiedź Marca, aby używała rzeczywistych liczb losowych i faktycznie wykonywała tę samą pracę we wszystkich przypadkach.
Wyniki:
Opracowano jako wydanie na podstawie VS 2008 SP1. Działa bez debugowania na [email protected], .NET 3.5 SP1.
Kod:
źródło
Pomiary są dobre, ale będziesz uzyskiwać znacznie różne wyniki w zależności od tego, co robisz dokładnie w swojej wewnętrznej pętli. Zmierz swoją sytuację. Jeśli używasz wielowątkowości, samo to jest nietrywialne.
źródło
Rzeczywiście, jeśli wykonasz kilka skomplikowanych obliczeń w pętli, wydajność indeksatora tablic w porównaniu z indeksatorem list może być tak nieznacznie mała, że ostatecznie nie ma to znaczenia.
źródło
Oto jeden, który używa Słowników, IEnumerable:
źródło
Nie próbuj zwiększać pojemności poprzez zwiększenie liczby elementów.
Występ
źródło
Martwiłem się, że testy porównawcze zamieszczone w innych odpowiedziach nadal pozostawią miejsce dla kompilatora w celu optymalizacji, eliminacji lub scalania pętli, więc napisałem taki, który:
W rezultacie bezpośrednia tablica ma około 250% lepszą wydajność niż dostęp do tablicy owiniętej w IList:
Oto kod:
źródło
Ponieważ List <> używa tablic wewnętrznie, podstawowa wydajność powinna być taka sama. Dwa powody, dla których lista może być nieco wolniejsza:
Aby sprawdzić, czy ma to dla Ciebie znaczenie, prawdopodobnie najlepiej dopasuj opublikowane funkcje synchronizacji do listy o rozmiarze, którego planujesz użyć, i zobacz, jakie są wyniki dla twojego specjalnego przypadku.
źródło
Ponieważ miałem podobne pytanie, zapewniłem sobie szybki start.
Moje pytanie jest nieco bardziej szczegółowe: „jaka jest najszybsza metoda implementacji macierzy zwrotnej”
Testy przeprowadzone przez Marca Gravella pokazują wiele, ale nie dokładnie określają czas. Jego wyczucie czasu obejmuje również zapętlanie tablicy i list. Ponieważ wymyśliłem także trzecią metodę, którą chciałem przetestować, czyli „Słownik”, dla porównania, rozszerzyłem kod testowy hist.
Najpierw wykonuję test przy użyciu stałej, która daje mi pewien czas, w tym pętlę. Jest to czas „goły”, z wyłączeniem faktycznego dostępu. Następnie wykonuję test, uzyskując dostęp do struktury tematu, co daje mi i taktowanie, zapętlenie i rzeczywisty dostęp.
Różnica między taktowaniem „nagim” a czasem „wykluczonym z góry” daje mi wskazanie taktowania „dostępu do struktury”.
Ale jak dokładny jest ten czas? Podczas testu Windows wykona krojenie czasu dla shure. Nie mam żadnych informacji na temat podziału czasu, ale zakładam, że jest on równomiernie rozłożony podczas testu i rzędu dziesiątek ms, co oznacza, że dokładność pomiaru czasu powinna być rzędu +/- 100 ms lub więcej. Trochę przybliżone oszacowanie? W każdym razie źródło systematycznego błędu mearure.
Ponadto testy przeprowadzono w trybie „Debugowanie” bez optymalizacji. W przeciwnym razie kompilator może zmienić rzeczywisty kod testowy.
Otrzymuję więc dwa wyniki, jeden dla stałej oznaczonej „(c)”, a drugi dla dostępu oznaczony „(n)”, a różnica „dt” mówi mi, ile czasu zajmuje faktyczny dostęp.
A oto wyniki:
Przy lepszych oszacowaniach błędów synchronizacji (jak usunąć systematyczny błąd pomiaru z powodu podziału czasu?) Można by powiedzieć więcej o wynikach.
Wygląda na to, że Lista / Foreach ma najszybszy dostęp, ale koszty ogólne go zabijają.
Różnica między List / for i List / foreach jest dziwna. Może w grę wchodzi gotówka?
Ponadto, aby uzyskać dostęp do tablicy, nie ma znaczenia, czy używasz
for
pętli czyforeach
pętli. Wyniki pomiaru czasu i jego dokładność sprawiają, że wyniki są „porównywalne”.Korzystanie ze słownika jest zdecydowanie najwolniejsze, zastanawiałem się tylko nad tym, ponieważ po lewej stronie (indeksator) mam rzadką listę liczb całkowitych, a nie zakres używany w tych testach.
Oto zmodyfikowany kod testowy.
źródło
W niektórych krótkich testach odkryłem, że kombinacja tych dwóch jest lepsza w tak zwanej dość intensywnej matematyce:
Rodzaj:
List<double[]>
Rodzaj:
List<List<double>>
Rodzaj:
double[rows * columns]
Uruchamianie kodu:
Chciałbym, abyśmy mieli jakieś najwyższej klasy klasy akceleracji sprzętowej, takie jak zespół .NET z
System.Numerics.Vectors
klasą!C # może być najlepszym językiem ML z odrobiną więcej pracy w tym obszarze!
źródło