Acest articol este un articol oglindă al traducerii automate, vă rugăm să faceți clic aici pentru a sări la articolul original.

Vedere: 6846|Răspunde: 2

[Sursă] [Întoarce]. Optimizarea performanței .NET - Collections. Pooled este recomandat

[Copiază linkul]
Postat la 29-05-2022 13:45:22 | | | |
Scurtă introducere

Optimizarea performanței este modul în care se asigură că același număr de cereri sunt procesate cu mai puține resurse, care sunt în general CPU sau memorie, și, desigur, handle-uri ale IO-ului sistemului de operare, traficul de rețea, utilizarea discului etc. Dar, de cele mai multe ori, reducem consumul de CPU și memorie.
Conținutul împărtășit anterior are unele limitări, este dificil de transformat direct, astăzi vreau să vă împărtășesc o metodă simplă: trebuie înlocuit doar câteva tipuri de colecții, pentru a obține efectul de îmbunătățire a performanței și reducerea consumului de memorie.
Astăzi vreau să vă împărtășesc o bibliotecă de clasă, aceastaBiblioteca de clase se numește Collections.Pooled, După cum se poate vedea din nume, este prin memorie pooled pentru a atinge scopul de a reduce amprenta de memorie și GC, și vom vedea direct cum este performanța sa, iar de asemenea vă vom conduce să vedeți codul sursă, de ce aduce aceste îmbunătățiri de performanță.

Colecții. Grupate

Link pentru proiect:Autentificarea cu hyperlink este vizibilă.

Biblioteca se bazează pe clase din System.Collections.Generic, care au fost modificate pentru a profita de <T>noile biblioteci de clase System.Span și System.Buffers.ArrayPool<T>, cu scopul de a reduce alocarea memoriei, de a îmbunătăți performanța și de a permite o interoperabilitate mai mare cu API-urile moderne.
Collections.Pooled suportă .NET Standnd 2.0 (.NET Framework 4.6.1+), precum și suport pentru . NET Core 2.1+. Un set extins de teste unitare și benchmark-uri a fost portat de pe CoreFX.

Numărul total de teste: 27501. Prin: 27501. Eșec: 0. Sărite: 0.
Testul a fost un succes.
Timp de execuție al testului: 9,9019 secunde

Cum să folosești

Poți instala ușor această bibliotecă prin Nuget, NuGet Version.

În biblioteca Collections.Pooled, implementează versiuni comune pentru tipurile de colecții pe care le folosim frecvent, așa cum se arată în comparația cu tipurile native .NET.

. .NET nativColecții. Grupateremarca
Listă<T>PooledList<T>Clase generice de colecție
Dicționar<TKey, TValue>PooledDictionary<TKey, TValue>Clasă generică de dicționar
HashSet<T>PooledSet<T>Clase generice de colecție hash
Stivă<T>Stivă<T>Stack-uri generice
Coadă<T>PooledQueue<T>Cohortă generică

Când folosim, trebuie doar să adăugăm . Versiunea nativă .NET cu versiunea Collections.Pooled, așa cum se arată în codul de mai jos:

Totuși, trebuie să menționăm că tipul Pooled implementează interfața IDispose, care returnează memoria folosită pool-ului prin metoda Dispose(), așa că trebuie să apelăm metoda Dispose() după folosirea obiectului de colecție Pooled. Sau poți folosi cuvântul cheie using var direct.

Notă: Folosește obiectul colecție din Collections.PooledCel mai bine este să fie nevoie să-l eliberezi manual, dar nu contează dacă nu îl eliberezi, GC-ul îl va recicla în cele din urmă, dar nu poate fi returnat în pool și nu va obține efectul de economisire a memoriei.
Deoarece reutilizează spațiul de memorie, atunci când returnează spațiul de memorie în pool, trebuie să proceseze elementele din colecție și oferă o enumerare numită ClearMode pentru utilizare, definită astfel:




Implicit, poți folosi valoarea implicită Auto, iar dacă există cerințe speciale de performanță, poți folosi Never după ce ai aflat riscurile.
Pentru tipurile de referință și tipurile de valori care conțin tipuri de referință, trebuie să golim referința de tip array atunci când returnăm spațiul de memorie în pool, dacă nu este șters, GC nu va putea elibera această parte a spațiului de memorie (deoarece referința elementului a fost întotdeauna păstrată de pool), dacă este un tip de valoare pură, atunci nu poate fi golită. În acest articol descriu diferența de stocare dintre tipurile de referință și array-urile struct (tip valoare), tipurile de valoare pure nu au reciclarea antetului obiectului și nu necesită intervenția GC.


. Optimizarea performanței .NET - Folosește clase alternative struct:Autentificarea cu hyperlink este vizibilă.

Comparație de performanțe

Nu am făcut Benchmark singur, iar rezultatele scorului de rulare ale proiectelor open source pe care le-am folosit direct au fost 0 pentru utilizarea memoriei multor proiecte, ceea ce s-a întâmplat pentru că memoria în pool nu avea alocare suplimentară.

PooledList<T>

Parcurge în buclă cele 2048 de elemente adăugate în set în Benchmark, . .NET native <T>List necesită 110us (conform rezultatelor reale ale benchmark-ului, milisecundele din figură ar trebui să fie o eroare administrativă) și 263KB memorie, în timp ce PooledList <T>are nevoie doar de 36us și 0KB de memorie.




PooledDictionary<TKey, TValue>

Adaugă 10_0000 de elemente în dicționar într-o buclă în Benchmark, . .NET native Dictionary<TKey, TValue> necesită 11ms și 13MB memorie, în timp ce PooledDictionary<TKey, TValue> necesită doar 7ms și 0MB de memorie.




PooledSet<T>

Parcurge în buclă colecția de hash-uri din Benchmark și adaugă 10_0000 elemente, . HashSet-ul nativ .<T>NET necesită 5348ms și 2MB, în timp ce PooledSet <T>necesită doar 4723ms și 0MB de memorie.




PooledStack<T>

Parcurge stiva în Benchmark pentru a adăuga 10_0000 elemente, . PooledStack nativ <T>.NET necesită 1079ms și 2MB, în timp ce <T>PooledStack necesită doar 633ms și 0MB de memorie.




PooledQueue<T>

Parcurge bucla prin bucle din Benchmark pentru a adăuga 10_0000 elemente la coadă, . <T>.NET nativ PooledQueue necesită 681ms și 1MB, în timp ce PooledQueue <T>necesită doar 408ms și 0MB de memorie.




Scena nu este eliberată manual

În plus, am menționat mai sus că tipul colectării comune trebuie lansat, dar nu contează dacă nu este eliberat, pentru că GC-ul se va recicla.


Rezultatele Benchmark sunt următoarele:



Concluzia poate fi trânsă din rezultatele Benchmark-ului de mai sus.

Lansarea colecției de tipuri Pooled la timp abia declanșează GC și alocă memorie, iar din graficul de mai sus alocă doar 56Byte de memorie.
Chiar dacă colecția de tipuri Pooled nu este eliberată, deoarece alocă memorie din pool, va reutiliza memoria în timpul operațiunii de extindere ReSize și va sări peste pasul de inițializare a alocării memoriei GC, care este relativ rapid.
Cel mai lent este să folosești tipul normal de colecție, fiecare operație de extindere ReSize trebuie să aplice pentru spațiu de memorie nou, iar GC-ul trebuie, de asemenea, să recupereze spațiul de memorie anterior.


Analiza principiilor

Dacă ai citit postarea mea anterioară pe blog, ar trebui să setezi dimensiunea inițială pentru tipurile de colecții și să analizezi principiul de implementare al dicționarului C#, poți ști că dezvoltatorii BCL .NET folosesc structurile de date de bază ale acestor tipuri de colecții de bază ca tablouri pentru acces aleator de înaltă performanță, să luăm <T>List ca exemplu.

Creează un nou tablou pentru a stoca elementele adăugate.
Dacă nu există suficient spațiu în tablou, operația de expansiune este declanșată pentru a solicita de două ori mai mult spațiu.
Codul constructorului este următorul și puteți vedea că este un tablou generic creat direct:


Așadar, dacă vrei să faci pool de memorie, trebuie doar să schimbi locul unde este folosită noua aplicație-cheie în biblioteca de clase pentru a folosi aplicația pooled. Aici o împărtășesc cu voi. NET BCL este un tip numit ArrayPool, care oferă un pool de resurse de tablouri de instanțe generice reutilizabile, ce pot fi folosite pentru a reduce presiunea asupra GC și pentru a îmbunătăți performanța în cazul creării și distrugerii frecvente a tablourilor.

Stratul de bază al tipului nostru Pooled este să folosească ArrayPool pentru a partaja pool-uri de resurse, iar din constructorul său putem vedea că folosește ArrayPool implicit<T>. Partajat pentru a atribui obiecte de tip array și, desigur, poți crea propriul tău ArrayPool pentru a-l folosi.


În plus, la efectuarea unei operații de ajustare a capacității (extindere), vechiul tablou este returnat în pool-ul de fire, iar noul array este de asemenea achiziționat din pool.

În plus, autorul folosește Span pentru a optimiza API-uri precum Add și Insert, oferindu-le performanțe mai bune la accesul aleatoriu. În plus, a fost adăugat API-ul seriei TryXXX, astfel încât îl poți folosi într-un mod mai convenabil. De exemplu, <T>clasa List <T>are până la 170 de modificări comparativ cu PooledList.



rezumat

În utilizarea noastră online propriu-zisă, putem înlocui tipul nativ de colecție cu tipul de colecție oferit de Pooled, ceea ce este foarte util în reducerea consumului de memorie și a latenței P95.
De asemenea, chiar dacă uiți să-l lansezi, performanța nu va fi mult mai slabă decât folosind tipul nativ de colecție. Desigur, cel mai bun obicei este să-l eliberezi la timp.


Original:Autentificarea cu hyperlink este vizibilă.




Precedent:RecyclableMemoryStream oferă streaming .NET de înaltă performanță
Următor:[Luptă practică] Serverul construiește LibreSpeed pentru a testa viteza rețelei
Postat la 29-05-2022 17:12:36 |
Învață-l
Postat la 20-06-2022 09:09:22 |
Învață mixul
Disclaimer:
Tot software-ul, materialele de programare sau articolele publicate de Code Farmer Network sunt destinate exclusiv scopurilor de învățare și cercetare; Conținutul de mai sus nu va fi folosit în scopuri comerciale sau ilegale, altfel utilizatorii vor suporta toate consecințele. Informațiile de pe acest site provin de pe Internet, iar disputele privind drepturile de autor nu au legătură cu acest site. Trebuie să ștergi complet conținutul de mai sus de pe calculatorul tău în termen de 24 de ore de la descărcare. Dacă îți place programul, te rugăm să susții software-ul autentic, să cumperi înregistrarea și să primești servicii autentice mai bune. Dacă există vreo încălcare, vă rugăm să ne contactați prin e-mail.

Mail To:help@itsvse.com