Trumpas įvadas
System.Collections.Generic.List <T>yra bendra .NET rinkimo klasė, kuri dėl savo patogumo ir turtingos API gali saugoti bet kokio tipo duomenis, kuri plačiai naudojama mūsų kasdieniame gyvenime ir gali būti laikoma dažniausiai naudojama rinkimo klase.
Rašydami kodą, dažnai turime kartoti sąrašo <T>rinkinį, kad gautume jame esančius elementus tam tikram verslo apdorojimui. Paprastai rinkinyje nėra daug elementų ir jis labai greitai pereinamas. Tačiau kai kuriems dideliems duomenų apdorojimui, statistikai, skaičiavimui realiuoju laiku ir kt.<T>, kaip greitai pereiti dešimčių tūkstančių ar šimtų tūkstančių duomenų sąrašą? Štai ką aš turiu pasidalinti su jumis šiandien.
Perėjimo režimas
Pažvelkime į skirtingų perėjimo metodų našumą ir sukurkime šį našumo etaloną, naudodami skirtingos eilės rinkimo traversalą, kad pamatytume skirtingų metodų našumą. Kodo fragmentas atrodo taip:
Naudokite sakinį foreach
foreach yra labiausiai paplitęs būdas pereiti rinkinius, tai yra iteratoriaus modelio sintaksė cukraus įgyvendinimas, taip pat naudojamas kaip šio laiko etalonas.
Kadangi foreach sakinys yra sintaksės cukrus, kompiliatorius galiausiai iškviečia GetEnumerator() ir MoveNext() su while ciklu, kad įdiegtų funkciją. Sukompiliuotas kodas atrodo taip:
MoveNext() metodo įgyvendinimas užtikrins, kad iteracijoje nebus jokių kitų gijų, keičiančių rinkinį, o jei pakeitimas įvyks, jis pateiks InvalidOperationException išimtį ir turės perpildymo patikrinimą, kad patikrintų, ar dabartinis indeksas yra teisėtas, taip pat turės priskirti atitinkamą elementą surašytojui. Dabartinis atributas,Taigi iš tikrųjų jo našumas nėra pats geriausias, kodo fragmentas atrodo taip:
Pažvelkime, kaip jis veikia skirtinguose rinkiniuose ir rezultatai atrodo taip:
Galima pastebėti, kad esant skirtingiems dydžiams, reikalingas daug laiko reikalaujančio proceso linijinis augimo ryšys, net jei jis keliauja per 100 W duomenų be jokios apdorojimo logikos, tam reikia mažiausiai 1 s.
Sąrašo metodo ForEach naudojimas
Kitas įprastas būdas yra naudoti sąrašą<T>. ForEach() metodas, leidžiantis perduoti veiksmo <T>atstovą, kuris iškvies veiksmo atstovą, kai jis kartoja elementą<T>.
Tai <T>vidinis sąrašo diegimo būdas, todėl jis gali tiesiogiai pasiekti privačius masyvus ir išvengti perpildymo patikrinimų. Teoriškai jis turėtų būti greitas; Tačiau mūsų scenarijuje yra tik vienas tuščias metodas, kuris gali neveikti gerai su visiškai įterptu iškvietimu į foreach metodą. Žemiau pateikiamas metodo ForEach šaltinio kodas, kuris rodo, kad jame nėra perpildos tikrinimo, tačiau jis vis tiek išlaiko lygiagrečią versijos numerio tikrinimą.
Be to, kadangi reikia perduoti atstovą ForEach metodui, skambučio kode jis kiekvieną kartą patikrins, ar uždarymo generavimo klasės delegato objektas yra tuščias, o jei ne, naujas Action<T>(), kaip parodyta toliau:
Pažvelkime, kaip jis lyginamas su raktiniu žodžiu foreach našumo požiūriu. Toliau pateiktame paveikslėlyje parodyti lyginamojo indekso rezultatai:
Sprendžiant iš testo rezultatų, jis yra 40% lėtesnis nei tiesiogiai naudojant raktinį žodį foreach, atrodo, kad jei tai nėra būtina, geriau naudoti foreach tiesiogiai, taigi ar yra greitesnis būdas?
kilpos perėjimui
Grįžtant prie mūsų seniausio būdo, kuris yra naudoti raktažodį for pereiti kolekciją. Šiuo metu tai turėtų būti geriausiai veikiantis perėjimo metodas, nes jam nereikia perteklinio kodo, kaip ankstesniems (nors indeksavimo priemonė taip pat tikrinama, kad būtų išvengta perpildymo), ir akivaizdu, kad jis netikrina versijos numerio, todėl daugiagijoje, aplinkoje kolekcija yra keičiama, ir nebus jokių išimčių, kai naudojama. Testo kodas atrodo taip:
Pažiūrėkime, kaip tai pasirodys.
Atrodo, kad taip ir tikimės.Tiesioginis for ciklo naudojimas yra 60 % greitesnis nei foreach, rinkinys, kuris anksčiau užtrukdavo 1 sekundę, dabar trunka tik 400 milisekundžių. Taigi ar yra greitesnis būdas?
Naudokite kolekcijasMaršalas
Po .NET5 dotnet bendruomenė įdiegė CollectionsMarshal klasę, kad pagerintų inkasavimo operacijų našumą. Ši klasė įgyvendina, kaip pasiekti vietinius rinkinių tipų masyvus (jei matėte mano [. .NET našumo optimizavimas - turėtumėte nustatyti pradinį rinkinio tipų dydį] straipsnis, jūs žinote, kad daugelio duomenų struktūrų įgyvendinimas yra masyvai). Taigi jis gali praleisti visų rūšių aptikimus ir tiesiogiai pasiekti pradinį masyvą, kuris turėtų būti greičiausias. Kodas atrodo taip:
Matote, kad kompiliatoriaus sugeneruotas kodas yra labai efektyvus.
Tiesioginė prieiga prie pagrindinio masyvo yra labai pavojinga, turite žinoti, ką darote su kiekviena kodo eilute, ir pakankamai testuoti. Lyginamojo indekso rezultatai yra tokie:
OhoNaudojant CollectionsMarshal yra 79% greičiau nei naudojant foreach, bet tai turėtų būti JIT optimizavimo priežastis, nėra didelio skirtumo tarp foreach ir raktažodžių ciklo Span.
suvestinė
Šiandien kalbėjau su jumis apie tai, kaip greitai pereiti sąrašo kolekciją, ir daugeliu atvejų rekomenduojama naudoti raktinį žodį foreach, kuris turi ir perpildymo tikrinimą, ir kelių gijų versijos numerio valdymą, kuris gali padėti mums lengviau parašyti teisingą kodą.
Jei jums reikia didelio našumo ir didelio duomenų kiekio, rekomenduojama naudoti ir CollectionsMarshal.AsSpan tiesiogiai, kad galėtumėte pereiti rinkinį.
Šio straipsnio šaltinio kodo nuoroda:
Hipersaito prisijungimas matomas.
Originali nuoroda:Hipersaito prisijungimas matomas.
|