Kratek uvod
System.Collections.Generic.List <T>je generični razred zbirk v .NET, ki lahko shranjuje katerokoli vrsto podatkov zaradi priročnosti in bogatega API-ja, ki se pogosto uporablja v našem vsakdanjem življenju in velja za najbolj uporabljen razred zbirk.
Pri pisanju kode moramo pogosto iterirati <T>skozi zbirko List, da pridobimo elemente v njej za nekatere poslovne procese. Običajno v kompletu ni veliko elementov in je zelo hitro prečkati. Ampak za obdelavo velikih podatkov, statistiko, računalništvo v realnem času itd<T>., kako hitro prečkati zbirko desettisočev ali stotisočev podatkov? To moram danes deliti z vami.
Način prehoda
Poglejmo si zmogljivost različnih metod prečkanja in zgradimo naslednji referenčni kazalnik zmogljivosti, pri čemer uporabimo prehajanje zbiranja različnih velikostnih redov, da preverimo uspešnost različnih metod. Izsek kode izgleda takole:
Uporabite izjavo foreach
foreach je najpogostejši način prečkanja zbirk, gre za implementacijo iteratorskega vzorca s sintaktičnim sladkorjem in se uporablja tudi kot referenčna točka za ta čas.
Ker je ukaz foreach sintaktični sladkor, prevajalnik na koncu pokliče GetEnumerator() in MoveNext() z while zanko za implementacijo funkcionalnosti. Prevedena koda izgleda takole:
Implementacija metode MoveNext() bo zagotovila, da ne bo drugih niti, ki bi spreminjale zbirko v iteraciji, in če do spremembe pride, bo sprožila izjemo InvalidOperationException, imela bo overflow check za preverjanje, ali je trenutni indeks legitimen, poleg tega pa mora ustrezni element dodeliti enumeratorju. Trenutni atribut,Torej v resnici njegova zmogljivost ni najboljša, izsek kode izgleda takole:
Poglejmo, kako deluje v različnih velikostih setov, in rezultati so videti takole:
Vidimo lahko, da je pri različnih velikostih potrebna linearna rastna relacija časovno potratnega procesa, tudi če prehaja 100 W podatkov brez kakršnekoli procesne logike, traja vsaj 1 sekunde.
Uporabite metodo seznama ForEach
Drug pogost način je uporaba <T>Seznama. Metoda ForEach(), ki vam omogoča, da posredujete delegata akcije<T>, ki bo klical delegata akcije, ko ta iterira skozi element<T>.
Gre za <T>notranjo implementacijsko metodo Lista, zato lahko neposredno dostopa do zasebnih polj in se izogne preverjanju presečenja. V teoriji bi moralo biti hitro; V našem primeru pa obstaja le ena prazna metoda, ki se morda ne obnaša dobro pri popolnoma inline klicu na foreach metodo. Spodaj je izvorna koda metode ForEevery (ForEEvery metode), ki kaže, da nima preverjanja presežka, vendar še vedno ohranja sočasno preverjanje številk različice.
Poleg tega, ker je potrebno posredovati delegata metodi ForEach v klicni kodi, bo ta preverila, ali je objekt delegate v razredu generacije closure prazen vsakič, in če ne, nov Action<T>(), kot je prikazano spodaj:
Poglejmo, kako se primerja s ključno besedo foreach glede zmogljivosti. Naslednja slika prikazuje rezultate testa:
Sodeč po rezultatih testa je 40 % počasnejša od neposredne uporabe ključne besede foreach, zdi se, da če ni nujno, je boljša možnost uporabiti foreach neposredno, ali obstaja kakšna hitrejša rešitev?
za prehod skozi zanke
Če se vrnemo k najstarejšemu načinu, ki je uporaba ključne besede for za prečkanje zbirke. Trenutno bi moral biti to najbolj učinkovit način prehajanja, ker ne zahteva odvečne kode kot prejšnje (čeprav je indeksator prav tako preverjen, da prepreči prelivanja), in seveda ne preverja številke različice, zato se v večnitnem okolju zbirka spremeni in ne bo nobene izjeme pri uporabi for. Testna koda izgleda takole:
Poglejmo, kako se bo izteklo.
Zdi se, da je to način, kot pričakujemo.Uporaba zanke for neposredno je 60 % hitrejša kot foreach, komplet, ki je prej potreboval 1 sekundo za prehod, zdaj potrebuje le 400 milisekund. Torej, ali obstaja hitrejši način?
Uporaba zbirk Marshal
Po .NET5 je dotnet skupnost uvedla razred CollectionsMarshal, da bi izboljšala zmogljivost zbiralnih operacij. Ta razred implementira, kako dostopati do nativnih polj vrst zbirk (če ste videli moj [. .NET optimizacija zmogljivosti - Nastaviti morate začetno velikost za vrste zbirk, saj veste, da je osnovna implementacija mnogih podatkovnih struktur polja). Tako lahko preskoči vse vrste zaznav in neposredno dostopa do izvirnega polja, kar bi moralo biti najhitrejše. Koda izgleda takole:
Lahko vidite, da je koda, ki jo generira prevajalnik, zelo učinkovita.
Neposreden dostop do osnovnega polja je zelo nevaren, morate vedeti, kaj počnete z vsako vrstico kode, in imeti dovolj testiranja. Rezultati primerjalnih rezultatov so naslednji:
WowUporaba CollectionsMarshal je 79 % hitrejša kot foreach, vendar bi moral biti razlog za optimizacijo JIT, ni velike razlike med uporabo foreach in za keyword loop Span.
Povzetek
Danes sem se z vami pogovarjal o tem, kako hitro prečkati zbirko List, in v večini primerov je priporočljivo uporabiti ključno besedo foreach, ki omogoča tako preverjanje presežka kot večnitno upravljanje števila različic, kar nam lahko olajša pisanje pravilne kode.
Če potrebujete visoko zmogljivost in velike količine podatkov, je priporočljivo uporabiti for in CollectionsMarshal.AsSpan neposredno za prečkanje zbirke.
Povezava do izvorne kode tega članka:
Prijava do hiperpovezave je vidna.
Izvirna povezava:Prijava do hiperpovezave je vidna.
|