Lyhyt johdanto
Suorituskyvyn optimointi varmistaa, että sama määrä pyyntöjä käsitellään vähemmillä resursseilla, jotka ovat yleensä CPU:ta tai muistia, ja tietysti käyttöjärjestelmän IO-käsittelyt, verkkoliikenne, levyn käyttö jne. Mutta useimmiten vähennämme prosessorin ja muistin käyttöä. Aiemmin jaetulla sisällöllä on joitakin rajoituksia, suora muuntaminen on vaikeaa, tänään haluan jakaa kanssanne yksinkertaisen menetelmän, joka tarvitsee vain vaihtaa muutamia kokoelmatyyppejä, jotta suorituskyky paranee ja muistin jalanjälje pienenee. Tänään haluan jakaa kanssanne luokkakirjaston, tämänLuokkakirjasto on nimeltään Collections. PooledKuten nimestä näkyy, se on tarkoitettu yhteismuistin kautta muistin ja GC:n vähentämiseksi, ja näemme suoraan, miten sen suorituskyky on, ja viemme sinut myös lähdekoodiin, miksi se tuo nämä suorituskyvyn parannukset.
Kokoelmat. Yhteiset
Projektin linkki:Hyperlinkin kirjautuminen on näkyvissä.
Kirjasto perustuu System.Collections.Generic-luokan luokkiin, joita on muokattu hyödyntämään uusia System.Span<T>- ja System.Buffers.ArrayPool <T>-luokkakirjastoja muistin varautumisen vähentämiseksi, suorituskyvyn parantamiseksi ja paremman yhteentoimivuuden mahdollistamiseksi nykyaikaisten rajapintojen kanssa. Collections.Pooled tukee .NET Standnd 2.0 (.NET Framework 4.6.1+) sekä tukea . NET Core 2.1+. Laaja joukko yksikkötestejä ja testejä on portattu CoreFX:ltä.
Testien kokonaismäärä: 27501. Lähettäjä: 27501. Epäonnistuminen: 0. Ohi: 0. Koeajo onnistui. Testin suoritusaika: 9,9019 sekuntia
Käyttöohje
Voit helposti asentaa tämän kirjaston Nugetin kautta, NuGet Version.
Collections.Pooled-kirjastossa se toteuttaa poolatut versiot niille kokoelmatyypeille, joita yleisesti käyttämme, kuten vertailussa .NET-natiivityyppeihin nähtiin.
| . .NET-natiivi | Kokoelmat. Yhteiset | huomautus | | Lista<T> | PooledList<T> | Geneeriset kokoelmaluokat | | Sanakirja<TKey, TValue> | PooledDictionary<TKey, TValue> | Yleinen sanakirjaluokka | | HashSet<T> | PooledSet<T> | Yleiset hajautuskokoelmaluokat | | Pino<T> | Pino<T> | Yleiset pinot | | Jono<T> | PooledQueue<T> | Yleinen kohortti |
Kun käytämme , meidän tarvitsee vain lisätä vastaava . .NET-natiiviversio Collections.Pooled-version kanssa, kuten alla olevassa koodissa näkyy:
On kuitenkin huomattava, että Pooled-tyyppi toteuttaa IDispose-rajapinnan, joka palauttaa käytetyn muistin pooliin Dispose()-menetelmällä, joten meidän täytyy kutsua sen Dispose()-metodi Pooled-kokoelmaobjektin käytön jälkeen. Tai voit käyttää suoraan 'using var' -avainsanaa.
Huomautus: Käytä kokoelma-objektia Collections.Pooled-tiedostossaOn parasta vapauttaa se manuaalisesti, mutta sillä ei ole väliä, vaikka et julkaise sitä, GC kierrättää sen lopulta, mutta sitä ei voi palauttaa pooliin, eikä se saavuta muistin säästämisen vaikutusta. Koska se käyttää muistitilaa uudelleen, palauttaessaan muistitilaa pooliin sen täytyy käsitellä kokoelman alkiot, ja se tarjoaa käyttöön ClearMode-nimisen luettelon, joka määritellään seuraavasti:
Oletuksena voit käyttää oletusarvoa Auto, ja jos suorituskykyvaatimuksia on erityisiä, voit käyttää Nover-toimintoa riskien tietämisen jälkeen. Referenssityypeille ja viitetyyppejä sisältäville arvotyypeille meidän on tyhjennettävä taulukkoviittaus, kun palautetaan muistitila pooliin; jos sitä ei tyhjennetä, GC ei pysty vapauttamaan tätä osaa muistista (koska elementin viite on aina ollut poolin hallussa), jos kyseessä on puhdas arvotyyppi, sitä ei voi tyhjentää. Tässä artikkelissa kuvaan muistin eron viitetyyppien ja struct-(arvotyyppi) taulukoiden välillä; puhtailla arvotyypeillä ei ole objektiotsikoiden kierrätystä eivätkä ne vaadi GC:n puuttumista.
. .NET-suorituskyvyn optimointi – Käytä rakenne-vaihtoehtoisia luokkia:Hyperlinkin kirjautuminen on näkyvissä.
Suorituskyvyn vertailu
En tehnyt Benchmarkia yksin, ja avoimen lähdekoodin projektien juoksevat tulostulokset, joita käytin suoraan, olivat 0 monien projektien muistinkulutuksessa, koska käytetty muisti ei ollut ylimääräistä varausta.
PooledList<T>
Käy läpi 2048 elementtiä, jotka on lisätty joukkoon Benchmarkissa, . .NET-natiivilista <T>vaatii 110us (todellisten benchmark-tulosten mukaan kuvan millisekunnit pitäisi olla kirjoitusvirhe) ja 263 KB muistia, kun taas PooledList <T>tarvitsee vain 36us ja 0 KB muistia.
PooledDictionary<TKey, TValue>
Lisää sanakirjaan 10_0000 alkiota silmukassa Benchmarkissa, . .NETin natiivisanakirja<TKey, TValue> vaatii 11 ms ja 13 Mt muistia, kun taas PooledDictionary<TKey, TValue> vaatii vain 7 ms ja 0 MB muistia.
PooledSet<T>
Käy läpi hash-kokoelma Benchmarkissa, lisää 10_0000 alkiota, . .NET-natiivi HashSet <T>vaatii 5348ms ja 2MB, kun taas PooledSet <T>vaatii vain 4723ms ja 0MB muistia.
PooledStack<T>
Käy pinon läpi Benchmarkissa lisätäksesi 10_0000 alkiota, . .NET-natiivi PooledStack <T>vaatii 1079ms ja 2MB, kun taas PooledStack <T>vaatii vain 633ms ja 0MB muistia.
PooledQueue<T>
Käy silmukat läpi Benchmarkissa lisätäksesi jonoon 10_0000 alkiota, . .NET-natiivi <T>PooledQueue vaatii 681ms ja 1MB, kun taas PooledQueue <T>vaatii vain 408ms ja 0MB muistia.
Kohtausta ei julkaista manuaalisesti
Lisäksi mainitsimme yllä, että poolattu kokoelmatyyppi täytyy vapauttaa, mutta sillä ei ole väliä, jos sitä ei julkaista, koska GC kierrättää.
Vertailutulokset ovat seuraavat:
Johtopäätös voidaan tehdä yllä olevista vertailutuloksista.
Pooled-tyypin kokoelman vapauttaminen ajoissa laukaisee juuri ja juuri GC:n ja varaa muistia, yllä olevasta kaaviosta se varaa vain 56 tavua muistia. Vaikka Pooled-tyypin kokoelmaa ei vapautettaisi, koska se varaa muistia poolista, se käyttää muistia uudelleen ReSize-laajennusoperaation aikana ja ohittaa GC-allokointimuistin alustusvaiheen, joka on suhteellisen nopea. Hitain tapa on käyttää normaalia kokoelmatyyppiä, jokainen ReSize-laajennusoperaatio tarvitsee hakea uutta muistitilaa, ja GC:n täytyy myös ottaa takaisin aiempi muistitila.
Periaateanalyysi
Jos olet lukenut aiemman blogikirjoitukseni, sinun tulisi asettaa kokoelmatyyppien alkuperäinen koko ja analysoida C#-sanakirjan toteutusperiaatetta; tiedät, että .NET BCL -kehittäjät käyttävät näiden peruskokoelmatyyppien taustalla olevia tietorakenteita korkean suorituskyvyn satunnaiskäyttöön, otetaan esimerkiksi Lista<T>.
Luo uusi taulukko lisätyille elementeille. Jos taulukossa ei ole tarpeeksi tilaa, laajennusoperaatio käynnistyy pyytämään kaksinkertaista tilaa. Konstruktorikoodi on seuraava, ja näet, että se on geneerinen taulukko, joka luodaan suoraan:
Jos siis haluat jakaa muistia, sinun tarvitsee vain vaihtaa paikka, jossa uusi avainsanasovellus on luokkakirjastossa, käyttääksesi pooled applicationia. Tässä jaan sen kanssasi. NET BCL on tyyppi nimeltä ArrayPool, joka tarjoaa uudelleenkäytettävien geneeristen instanssien taulukkoresurssipoolin, jota voidaan käyttää GC:n paineen vähentämiseen ja suorituskyvyn parantamiseen usein tapahtuvan taulukon luomisen ja tuhoamisen yhteydessä.
Pooled-tyypin taustalla oleva kerros on käyttää ArrayPoolia resurssipoolien jakamiseen, ja sen rakentajasta näemme, että se käyttää oletuksena ArrayPoolia<T>. Jaettu taulukon objektien määrittämiseen, ja tietysti voit myös luoda oman ArrayPoolin sitä varten.
Lisäksi, kun suoritetaan kapasiteetin säätöoperaatio (laajennus), vanha taulukko palautetaan säiepooliin, ja uusi taulukko saadaan myös poolista.
Lisäksi tekijä käyttää Spania optimoidakseen API-rajapintoja, kuten Add ja Insert, parantaakseen satunnaispääsyn suorituskykyä. Lisäksi TryXXX-sarjan API on lisätty, joten voit käyttää sitä kätevämmin. Esimerkiksi <T><T>List-luokassa on jopa 170 muutosta verrattuna PooledListiin.
yhteenveto
Varsinaisessa verkkokäytössämme voimme korvata natiivikokoelmatyypin Pooledin tarjoamalla kokoelmatyypillä, mikä on erittäin hyödyllistä muistin käytön ja P95-viiveen vähentämisessä. Lisäksi, vaikka unohtaisit julkaista sen, suorituskyky ei ole paljon huonompi kuin natiivikokoelmatyypin käyttö. Tietenkin paras tapa on päästää se irti ajoissa.
Alkuperäinen:Hyperlinkin kirjautuminen on näkyvissä.
|