Kort introduktion
Prestandaoptimering är hur man säkerställer att samma antal förfrågningar behandlas med färre resurser, vilka vanligtvis är CPU eller minne, och naturligtvis operativsystemets IO-handtag, nätverkstrafik, diskanvändning, etc. Men oftast minskar vi CPU- och minnesanvändningen. Det tidigare delade innehållet har vissa begränsningar, det är svårt att transformera direkt, idag vill jag dela med mig av en enkel metod, man behöver bara byta ut några få samlingstyper för att uppnå effekten av förbättrad prestanda och minskad minnesanvändning. Idag vill jag dela med mig av ett klassbibliotek, dettaKlassbiblioteket heter Collections.Pooled, Som namnet kan se är det genom poolat minne som uppnås syftet att minska minnesanvändningen och GC, och vi kommer direkt att se hur prestandan är, och vi tar dig också för att se källkoden och varför den ger dessa prestandaförbättringar.
Samlingar. Poolade
Projektlänk:Inloggningen med hyperlänken är synlig.
Biblioteket baseras på klasser i System.Collections.Generic, som har modifierats för att dra nytta av de nya <T>System.Span- och System.Buffers.ArrayPool-klassbiblioteken <T>i syfte att minska minnesallokeringen, förbättra prestandan och möjliggöra större interoperabilitet med moderna API:er. Collections.Pooled stöder .NET Stand 2.0 (.NET Framework 4.6.1+), samt stöd för . NET Core 2.1+. Ett omfattande antal enhetstester och benchmarks har portats från CoreFX.
Totalt antal tester: 27 501. Via: 27501. Misslyckande: 0. Skip: 0. Testkörningen var framgångsrik. Testexekveringstid: 9,9019 sekunder
Hur man använder
Du kan enkelt installera detta bibliotek via Nuget, NuGet Version.
I biblioteket Collections.Pooled implementerar den poolade versioner för de samlingstyper vi vanligtvis använder, vilket visas i jämförelsen med .NET:s native-typer.
| . .NET-inbyggt | Samlingar. Poolade | anmärkning | | Lista<T> | PooledList<T> | Generiska samlingsklasser | | Dictionary<TKey, TValue> | PooledDictionary<TKey, TValue> | Generisk ordboksklass | | HashSet<T> | PooledSet<T> | Generiska hashinsamlingsklasser | | Stack<T> | Stack<T> | Generiska stackar | | Kö<T> | PooledQueue<T> | Generisk kohort |
När vi använder behöver vi bara lägga till motsvarande . .NET-native version med Collections.Pooled-versionen, som visas i koden nedan:
Vi måste dock notera att Pooled-typen implementerar IDispose-gränssnittet, som returnerar det använda minnet till poolen via Dispose()-metoden, så vi måste anropa dess Dispose()-metod efter att ha använt Pooled collection-objektet. Eller så kan du använda nyckelordet using var direkt.
Obs: Använd samlingsobjektet i Collections.PooledDet är bäst att behöva släppa den manuellt, men det spelar ingen roll om du inte släpper den, GC kommer så småningom att återvinna den, men den kan inte återlämnas till poolen, och den kommer inte att uppnå effekten av att spara minne. Eftersom den återanvänder minnesutrymme behöver den bearbeta elementen i samlingen när den återlämnar minnesutrymmet till poolen, och den tillhandahåller en uppräkning kallad ClearMode för användning, definierad enligt följande:
Som standard kan du använda standardvärdet Auto, och om det finns särskilda prestandakrav kan du använda Never efter att ha känt till riskerna. För referenstyper och värdetyper som innehåller referenstyper måste vi tömma arrayreferensen när vi återlämnar minnesutrymmet till poolen, om det inte är rensat kan GC inte frigöra denna del av minnesutrymmet (eftersom referensen för elementet alltid har hållits av poolen), om det är en ren värdetyp kan den inte tömmas, i denna artikel beskriver jag lagringsskillnaden mellan referenstyper och struktur- (värdetyp) arrayer, rena värdetyper har inte objekthuvudsåtervinning och kräver ingen GC-ingripande.
. .NET prestandaoptimering - Använd strukturalternativa klasser:Inloggningen med hyperlänken är synlig.
Prestandajämförelse
Jag gjorde inte Benchmark ensam, och löpande poängresultaten för open source-projekt som jag direkt använde var 0 för minnesanvändningen i många projekt, vilket berodde på att det samlade minnet inte hade någon extra allokering.
PooledList<T>
Loop genom 2048-elementen som lagts till i mängden i Benchmark, . .NET:s native List <T>kräver 110 US (enligt de faktiska benchmarkresultaten ska millisekunderna i figuren vara ett skrivfel) och 263 KB minne, medan PooledList <T>bara behöver 36 US och 0 KB minne.
PooledDictionary<TKey, TValue>
Lägg till 10_0000 element i ordboken i en loop i Benchmark, . .NET:s inbyggda Dictionary<TKey, TValue> kräver 11 ms och 13 MB minne, medan PooledDictionary<TKey, TValue> bara kräver 7 ms och 0 MB minne.
PooledSet<T>
Loop genom hash-samlingen i Benchmark lägger till 10_0000 element, . .NET:s native HashSet <T>kräver 5348 ms och 2 MB, medan PooledSet <T>endast kräver 4723 ms och 0 MB minne.
PooledStack<T>
Loop genom stacken i Benchmark för att lägga till 10_0000 element, . .NET:s inbyggda PooledStack <T>kräver 1079 ms och 2 MB, medan PooledStack <T>endast kräver 633 ms och 0 MB minne.
PooledQueue<T>
Loop genom looparna i Benchmark för att lägga till 10_0000 element i kön, . .NET:s inbyggda <T>PooledQueue kräver 681 ms och 1 MB, medan PooledQueue <T>endast kräver 408 ms och 0 MB minne.
Scenen släpps inte manuellt
Dessutom nämnde vi ovan att den samlade samlingstypen måste släppas, men det spelar ingen roll om den inte släpps, eftersom GC:n kommer att återvinna.
Benchmarkresultaten är följande:
Slutsatsen kan dras från benchmarkresultaten ovan.
Att släppa den poolade typsamlingen i tid triggar knappt GC och allokerar minne, enligt ovanstående graf allokeras endast 56 byte minne. Även om Pooled-typsamlingen inte släpps, kommer den ändå att återanvända minne under ReSize-expansionsoperationen eftersom den allokerar minne från poolen och hoppa över GC-allokeringsminnesinitialiseringen, som är relativt snabb. Den långsammaste är att använda den normala samlingstypen, varje ReSize-expansionsoperation måste tillämpas för nytt minnesutrymme, och GC måste också återta det tidigare minnesutrymmet.
Principanalys
Om du har läst mitt tidigare blogginlägg bör du ställa in den initiala storleken för samlingstyper och analysera implementeringsprincipen för C# Dictionary, du kan veta att .NET BCL-utvecklare använder de underliggande datastrukturerna för dessa grundläggande samlingstyper som arrayer för högpresterande slumpmässig åtkomst, låt oss ta List <T>som exempel.
Skapa en ny array för att lagra de tillagda elementen. Om det inte finns tillräckligt med utrymme i arrayen triggas expansionsoperationen för att begära dubbla utrymmets storlek. Konstruktörkoden är som följer, och du kan se att det är en generisk array som skapats direkt:
Så om du vill poola minne behöver du bara ändra platsen där det nya nyckelordsprogrammet används i klassbiblioteket för att använda pooled application. Här delar jag med mig av den. NET BCL är en typ som kallas ArrayPool, som tillhandahåller en arrayresurspool av återanvändbara generiska instanser, vilka kan användas för att minska trycket på GC och förbättra prestandan vid frekvent arrayskapande och förstörelse.
Det underliggande lagret i vår Pooled-typ är att använda ArrayPool för att dela resurspooler, och från dess konstruktör kan vi se att den använder ArrayPool som standard<T>. Delat för att tilldela arrayobjekt, och självklart kan du också skapa din egen ArrayPool för att använda det.
Dessutom, vid en kapacitetsjusteringsoperation (expansion), återförs den gamla arrayen till trådpoolen, och den nya arrayen hämtas också från poolen.
Dessutom använder författaren Span för att optimera API:er som Add och Insert för att ge dem bättre prestanda vid slumpmässig åtkomst. Dessutom har TryXXX-seriens API lagts till, så du kan använda det på ett mer bekvämt sätt. Till exempel <T>har List-klassen <T>upp till 170 modifieringar jämfört med PooledList.
sammanfattning
I vår faktiska onlineanvändning kan vi ersätta den inbyggda samlingstypen med den samlingstyp som tillhandahålls av Pooled, vilket är mycket hjälpsamt för att minska minnesanvändning och P95-latens. Dessutom, även om du glömmer att släppa den, kommer prestandan inte att vara mycket sämre än att använda den inbyggda samlingstypen. Självklart är den bästa vanan att släppa ut det i tid.
Original:Inloggningen med hyperlänken är synlig.
|