Īss ievads
System.Collections.Generic.List <T>ir vispārēja kolekcijas klase .NET, kas var uzglabāt jebkura veida datus, pateicoties tās ērtībai un bagātīgajai API, kas tiek plaši izmantota mūsu ikdienas dzīvē, un var teikt, ka tā ir visbiežāk izmantotā kolekcijas klase.
Koda rakstīšanā mums bieži ir jāatkārto saraksta <T>kolekcija, lai iegūtu tajā esošos elementus biznesa apstrādei. Parasti komplektā nav daudz elementu, un tas ir ļoti ātri šķērsojams. Bet dažiem lieliem datu apstrādei, statistikai, reāllaika skaitļošanai utt.<T>, Kā ātri šķērsot desmitiem tūkstošu vai simtiem tūkstošu datu saraksta kolekciju? Tas ir tas, kas man šodien jādalās ar jums.
Šķērsošanas režīms
Apskatīsim dažādu šķērsošanas metožu veiktspēju un izveidosim šādu veiktspējas etalonu, izmantojot dažādas lieluma secības savākšanas šķērsošanu, lai redzētu dažādu metožu veiktspēju. Koda fragments izskatās šādi:
Priekšraksta foreach izmantošana
foreach ir visizplatītākais veids, kā mēs šķērsojam kolekcijas, tā ir iteratora modeļa sintakses cukura ieviešana, un to izmanto arī kā etalonu šim laikam.
Tā kā foreach priekšraksts ir sintakses cukurs, kompilators galu galā izsauc GetEnumerator() un MoveNext() ar cilpu while, lai ieviestu funkcionalitāti. Kompilētais kods izskatās šādi:
MoveNext() metodes ieviešana nodrošinās, ka iterācijā nebūs citu pavedienu, kas modificē kolekciju, un, ja modifikācija notiks, tā izmetīs InvalidOperationException izņēmumu, un tai būs pārplūdes pārbaude, lai pārbaudītu, vai pašreizējais indekss ir likumīgs, un tam ir arī jāpiešķir atbilstošais elements skaitītājam. Pašreizējais atribūts,Tātad patiesībā tā veiktspēja nav labākā, koda fragments izskatās šādi:
Apskatīsim, kā tas darbojas dažādos kopu lielumos, un rezultāti izskatās šādi:
Var redzēt, ka dažādu izmēru gadījumā ir nepieciešama laikietilpīgā procesa lineārā augšanas attiecība, pat ja tas šķērso 100 W datu bez apstrādes loģikas, tas aizņem vismaz 1 s.
Saraksta metodes ForEach izmantošana
Vēl viens izplatīts veids ir izmantot sarakstu<T>. ForEach() metode, kas ļauj nodot darbības <T>pārstāvi, kas izsauc darbības pārstāvi, kad tas atkārto elementu<T>.
Tā ir <T>saraksta iekšējā ieviešanas metode, tāpēc tā var tieši piekļūt privātiem masīviem un izvairīties no pārplūdes pārbaudēm. Teorētiski tam vajadzētu būt ātram; Bet mūsu scenārijā ir tikai viena tukša metode, kas var nedarboties labi ar pilnībā iekļautu izsaukumu uz foreach metodi. Zemāk ir metodes ForEach avota kods, kas parāda, ka tai nav pārplūdes pārbaudes, bet tā joprojām saglabā vienlaicīgu versijas numuru pārbaudi.
Turklāt, tā kā ir nepieciešams nodot pārstāvi metodei ForEach, zvana kodā tas pārbaudīs, vai delegāta objekts slēgšanas ģenerēšanas klasē katru reizi ir tukšs, un, ja nē, jauns Action<T>(), kā parādīts tālāk:
Apskatīsim, kā tas ir salīdzināms ar atslēgvārdu foreach veiktspējas ziņā. Tālāk redzamajā attēlā ir parādīti etalona rezultāti.
Spriežot pēc testa rezultātiem, tas ir par 40% lēnāks nekā tieši izmantojot foreach atslēgvārdu, šķiet, ka, ja tas nav nepieciešams, tā ir labāka izvēle izmantot foreach tieši, tāpēc vai ir kāds ātrāks veids?
cilpas šķērsošanai
Atgriežoties pie mūsu senākā veida, kas ir izmantot atslēgvārdu for, lai šķērsotu kolekciju. Šobrīd tai vajadzētu būt vislabākajai šķērsošanas metodei, jo tai nav nepieciešams kāds lieks kods kā iepriekšējie (lai gan indeksētājs tiek pārbaudīts arī, lai novērstu pārplūdes), un acīmredzot tas nepārbauda versijas numuru, tāpēc daudzpavedienu vidē kolekcija tiek mainīta, un, lietojot for, nebūs izņēmuma. Testa kods izskatās šādi:
Redzēsim, kā tas izrādās.
Šķiet, ka tas ir veids, kā mēs to sagaidām.Tieša for cilpas izmantošana ir par 60% ātrāka nekā foreach, komplekts, kura šķērsošana agrāk aizņēma 1 sekundi, tagad aizņem tikai 400 milisekundes. Tātad, vai ir ātrāks veids?
Izmantojiet kolekcijasMaršals
Pēc .NET5 dotnet kopiena ieviesa CollectionsMarshal klasi, lai uzlabotu savākšanas operāciju veiktspēju. Šī klase īsteno, kā piekļūt kolekcijas tipu vietējiem masīviem (ja esat redzējis manu [. .NET veiktspējas optimizācija - jums vajadzētu iestatīt sākotnējo lielumu kolekcijas veidiem] raksts, jūs zināt, ka daudzu datu struktūru pamatā ir masīvi). Tātad tas var izlaist visu veidu atklāšanu un tieši piekļūt sākotnējam masīvam, kam vajadzētu būt ātrākajam. Kods izskatās šādi:
Jūs varat redzēt, ka kompilatora ģenerētais kods ir ļoti efektīvs.
Tieša piekļuve pamatā esošajam masīvam ir ļoti bīstama, jums ir jāzina, ko jūs darāt ar katru koda rindu, un ir pietiekami daudz testēšanas. Etalona rezultāti ir šādi:
WowIzmantojot CollectionsMarshal ir par 79% ātrāk nekā izmantojot foreach, bet tam vajadzētu būt JIT optimizācijas iemeslam, nav lielas atšķirības starp foreach un atslēgvārdu cilpas Span izmantošanu.
Kopsavilkuma
Šodien es runāju ar jums par to, kā ātri šķērsot saraksta kolekciju, un vairumā gadījumu ieteicams izmantot foreach atslēgvārdu, kuram ir gan pārplūdes pārbaude, gan vairāku pavedienu versijas numura kontrole, kas var atvieglot pareizā koda rakstīšanu.
Ja jums ir nepieciešama augsta veiktspēja un liels datu apjoms, ieteicams izmantot un CollectionsMarshal.AsSpan tieši, lai šķērsotu kolekciju.
Šī raksta avota koda saite:
Hipersaites pieteikšanās ir redzama.
Oriģinālā saite:Hipersaites pieteikšanās ir redzama.
|