Kort introduktion
System.Collections.Generic.List <T>er en generisk samlingsklasse i .NET, som kan gemme enhver type data på grund af sin bekvemmelighed og rige API, som er udbredt i vores dagligdag, og som kan siges at være den mest brugte samlingsklasse.
I kodeskrivning skal vi ofte iterere gennem en <T>listesamling for at få elementerne i den til noget forretningsbehandling. Normalt er der ikke mange elementer i et sæt, og det er meget hurtigt at bevæge sig igennem. Men for noget big data-behandling, statistik, realtidscomputing osv.<T>, hvordan kan man så hurtigt navigere gennem listesamlingen med titusindvis eller hundredtusindvis af data? Det er det, jeg har brug for at dele med dig i dag.
Bevægelsestilstand
Lad os se på ydeevnen af forskellige traverseringsmetoder og bygge følgende performance benchmark, hvor vi bruger forskellige størrelsesorden i samlingstraversering for at se ydeevnen af forskellige metoder. Kodeudsnittet ser sådan ud:
Brug foreach-udsagnet
Foreach er den mest almindelige måde, vi gennemgår samlinger på, det er en syntaks-sugar-implementering af iterator-mønsteret, og det bruges også som benchmark for denne tid.
Da foreach-sætningen er et syntaks-sugar, kalder compileren til sidst GetEnumerator() og MoveNext() med en while-løkke for at implementere funktionaliteten. Den kompilerede kode ser sådan ud:
Implementeringen af MoveNext()-metoden sikrer, at der ikke er andre tråde, der ændrer samlingen i iterationen, og hvis ændringen sker, vil den kaste en InvalidOperationException-undtagelse, og den vil have en overflow-kontrol for at tjekke, om det aktuelle indeks er legitim, og den skal også tildele det tilsvarende element til enumeratoren. Nuværende attribut,Så faktisk er dens ydeevne ikke den bedste, kodeuddraget ser sådan ud:
Lad os se på, hvordan den præsterer på tværs af forskellige sætstørrelser, og resultaterne ser sådan ud:
Det kan ses, at i tilfælde af forskellige størrelser kræves det lineære vækstforhold i den tidskrævende proces, selv hvis den bevæger sig gennem 100w data uden nogen form for bearbejdningslogik, kræver det mindst 1 sekunder.
Brug ForEach-metoden i List
En anden almindelig måde er at bruge List<T>. ForEach()-metoden, som tillader dig at sende en <T>Action-delegeret ind, som kalder Action-delegeret, mens den itererer gennem elementet<T>.
Det er en <T>intern implementeringsmetode for List, så den kan få direkte adgang til private arrays og undgå overflow-kontroller. I teorien burde det være hurtigt; Men i vores scenarie er der kun én tom metode, som måske ikke fungerer godt med et fuldt inline kald til foreach-metoden. Nedenfor er kildekoden til ForEach-metoden, som viser, at den ikke har overflow-kontrol, men stadig samtidig versionsnummer-kontrol.
Derudover, da det er nødvendigt at sende en delegeret til ForEach-metoden, vil den i kaldkoden tjekke, om delegereobjektet i closure generation-klassen er tomt hver gang, og hvis ikke, ny Action<T>(), som vist nedenfor:
Lad os se på, hvordan det sammenlignes med foreach-nøgleordet i forhold til ydeevne. Følgende billede viser resultaterne af benchmarken:
Ud fra testresultaterne er det 40% langsommere end at bruge foreach-nøgleordet direkte, så det virker som om, hvis det ikke er nødvendigt, er det et bedre valg at bruge foreach direkte, så findes der nogen hurtigere måde?
for loop-gennemgang
Tilbage til vores ældste metode, som er at bruge for-nøgleordet til at bevæge sig gennem samlingen. Det burde være den bedst præsterende traverseringsmetode lige nu, fordi den ikke kræver redundant kode som de tidligere (selvom indekseren også er tjekket for at forhindre overflow), og selvfølgelig tjekker den ikke versionsnummeret, så i et multitrådet miljø ændres samlingen, og der vil ikke blive kastet nogen undtagelse, når man bruger for. Testkoden ser sådan ud:
Lad os se, hvordan det går.
Det ser ud til at være sådan, vi forventer det.At bruge for-løkken direkte er 60% hurtigere end foreach, et sæt, der tidligere tog 1 sekund at bevæge sig igennem, nu kun tager 400 millisekunder. Så findes der en hurtigere måde?
Brug samlingerMarshal
Efter .NET5 implementerede dotnet-fællesskabet CollectionsMarshal-klassen for at forbedre ydeevnen af samlingsoperationer. Denne klasse implementerer, hvordan man tilgår native arrays af samlingstyper (hvis du har set min [. .NET Performance Optimization - Du bør sætte den indledende størrelse for samlingstyper] artikel, du ved, at den underliggende implementering af mange datastrukturer er arrays). Så den kan springe alle slags detektioner over og direkte tilgå det oprindelige array, hvilket burde være det hurtigste. Koden ser sådan ud:
Du kan se, at koden genereret af compileren er meget effektiv.
Direkte adgang til det underliggende array er meget farligt, du skal vide, hvad du laver med hver linje kode, og have nok test. Benchmarkresultaterne er som følger:
WowAt bruge CollectionsMarshal er 79% hurtigere end at bruge foreach, men det burde være grunden til JIT-optimering, der er ikke den store forskel på at bruge foreach og for keyword loop Span.
resumé
I dag talte jeg med jer om, hvordan man hurtigt kan gennemgå List-samlingen, og i de fleste tilfælde anbefales det at bruge foreach-nøgleordet, som både har overflow-kontrol og multitrådet versionsnummerkontrol, hvilket kan gøre det lettere for os at skrive den korrekte kode.
Hvis du har brug for høj ydeevne og store datamængder, anbefales det at bruge for og CollectionsMarshal.AsSpan direkte til at gennemgå samlingen.
Kildekodelink til denne artikel:
Hyperlink-login er synlig.
Originalt link:Hyperlink-login er synlig.
|