Rövid bevezetés
A teljesítményoptimalizálás azt jelenti, hogy ugyanannyi kérés feldolgozása kerüljön kevesebb erőforrással, amelyek általában CPU-val vagy memóriával rendelkeznek, és természetesen az operációs rendszer IO-kezelései, hálózati forgalom, lemezhasználat stb. De legtöbbször csökkentjük a CPU és a memória használatát. A korábban megosztott tartalomnak vannak korlátai, nehéz közvetlenül átalakítani, ma egy egyszerű módszert szeretnék megosztani veled, csak néhány gyűjteménytípust kell kicserélni, hogy javítsd a teljesítményt és csökkentsd a memória igényét. Ma szeretném megosztani veletek egy osztálykönyvtárat, eztAz osztálykönyvtár neve Collections.PooledAhogy a névből is látszik, a csoportos memórián keresztül történik a memória és a GC csökkentése érdekében, és közvetlenül megnézzük, milyen teljesítmény, valamint elviszünk a forráskódot is, hogy miért hozza ezeket a teljesítményjavulásokat.
Gyűjtemények. Összegyűjtött
Projekt linkje:A hiperlink bejelentkezés látható.
A könyvtár a System.Collections.Generic osztályokon alapul, amelyeket módosítottak, hogy kihasználják az új System.Span <T>és System.Buffers.ArrayPool <T>osztálykönyvtárakat a memória leosztásának, a teljesítmény javításának és a modern API-kkal való nagyobb interoperabilitásnak való lehetővé tétele érdekében. A Collections.Pooled támogatja a .NET Standnd 2.0-t (.NET Framework 4.6.1+), valamint támogatja a . NET Core 2.1+. A CoreFX-ről kiterjedt egységtesztek és benchmarkok kerültek át.
A tesztek összesített száma: 27501. Via: 27501. Kudarc: 0. Skip: 0. A próbafutás sikeres volt. Teszt végrehajtási ideje: 9,9019 másodperc
Hogyan kell használni
Ezt a könyvtárat könnyen telepítheted Nuget vagy NuGet verzióval is.
A Collections.Pooled könyvtárban a leggyakrabban használt gyűjteménytípusok csoportos verzióit valósítja meg, ahogy azt a .NET natív típusokkal való összehasonlítás is mutatja.
| . .NET natív | Gyűjtemények. Összegyűjtött | megjegyzés | | Lista<T> | PooledList<T> | Általános gyűjteményosztályok | | Szótár<TKey, TValue> | PooledDictionary<TKey, TValue> | Általános szótárosztály | | HashSet<T> | PooledSet<T> | Általános hash gyűjtemény osztályok | | Boglya<T> | Boglya<T> | Általános vermek | | Sor<T> | PooledQueue<T> | Általános kohorsz |
Használatkor csak a hozzá tartozó . .NET natív verzió a Collections.Pooled verzióval, ahogy az alábbi kódban látható:
Ugyanakkor meg kell jegyeznünk, hogy a Pooled típus az IDispose interfészt valósítja meg, amely a használt memóriát a Dispose() metódóval visszaadja a poolba, ezért a Pooled gyűjtemény objektum használata után hívjuk a Dispose() metódust. Vagy közvetlenül a var használó kulcsszót használhatod.
Megjegyzés: Használd a gyűjtemény objektumot a Collections.Pooled területénA legjobb, ha kézzel kell kiadni, de nem számít, ha nem engeded ki, a GC végül újrahasznosítja, de nem lehet visszaállítani a poolba, és nem éri el a memória megtakarításának hatását. Mivel újrahasznosítja a memóriateret, amikor visszaadja a memória térét a poolba, fel kell dolgoznia a gyűjtemény elemeit, és egy ClearMode nevű felsorolást biztosít, amely a következőképpen definiálható:
Alapértelmezés szerint az alapértelmezett Auto értéket használhatod, és ha speciális teljesítménykövetelmények vannak, akkor a kockázatok ismerete után használhatod a Neso-t. Referenciatípusok és értéktípusok esetén, amelyek hivatkozási típusokat tartalmaznak, a tömbhivatkozást ki kell ürítenünk, amikor visszaadjuk a memória téret a poolba; ha nem tisztítják meg, a GC nem tudja felszabadítani ezt a memória részt (mert az elem hivatkozása mindig is a pool volt), ha tiszta értéktípus, akkor nem lehet kiüríteni; ebben a cikkben leírom a tároló különbséget a referenciatípusok és struktur (értéktípus) tömbök között, a tiszta értéktípusoknak nincs objektumfejléc-újrahasznosítása, és nem igényelnek GC beavatkozást.
. .NET teljesítményoptimalizálás – Használj struct alternatív osztályokat:A hiperlink bejelentkezés látható.
Teljesítményösszehasonlítás
Nem csináltam egyedül a Benchmarkot, és a nyílt forráskódú projektek futó pontszáma 0 volt sok projekt memóriahasználatában, mert a használt memóriának nem volt plusz lefoglalása.
PooledList<T>
Körbefutni a Benchmark-ban hozzáadott 2048 elemeket a készlethez. A .NET natív <T>listához 110us (a tényleges benchmark eredmények szerint az ábrán szereplő milliszekundumok írási hibának kellene lenniük) és 263KB memóriát igényelnek, míg a PooledList <T>csak 36us és 0KB memóriára van szükség.
PooledDictionary<TKey, TValue>
Hozzáadjunk 10_0000 elemet a szótárhoz egy körben a Benchmarkban, . A .NET natív szótár<TKey, TValue> 11ms és 13MB memóriát igényel, míg a PooledDictionary<TKey, TValue> csak 7ms és 0MB memóriát igényel.
PooledSet<T>
Körözz végig a hash-gyűjteményen a Benchmarkban, adj hozzá 10_0000 elemet, . A .NET natív HashSet <T>5348ms és 2 MB memóriát igényel, míg a PooledSet <T>csak 4723ms és 0 MB memóriát igényel.
PooledStack<T>
Körözz végig a stacken a Benchmarkban, hogy hozzáadj 10_0000 elemet, . A .NET natív PooledStack <T>1079 ms és 2 MB sebességet igényel, míg a PooledStack <T>csak 633 ms és 0 MB memóriát igényel.
PooledQueue<T>
Körbefut a Benchmark ciklusain, hogy 10_0000 elemet adj hozzá a sorhoz, . A .NET natív <T>PooledQueue 681ms és 1MB kapacitást igényel, míg a PooledQueue <T>csak 408ms és 0MB memóriát igényel.
A jelenet nem kerül kézzel kiadásra
Ezen felül említettük, hogy a csoportosított gyűjteménytípust ki kell adni, de nem számít, ha nem adják ki, mert a GC újrahasznosítja.
A benchmark eredményei a következők:
A következtetés a fenti benchmark eredményekből vonható le.
A Pooled típusú gyűjtemény időben történő kiszabadítása alig indítja el a GC-t és a memóriát is elosztja, a fenti grafikon alapján csak 56 bájtot oszt le. Még ha a Pooled típusú gyűjtemény nem is szabadul ki, mivel a medóriát a poolból osztja ki, a ReSize bővítési művelet során újra felhasználja a memóriát, és kihagyja a GC leallokált memória inicializációs lépését, ami viszonylag gyors. A leglassabb a normál gyűjteménytípus használata, minden ReSize bővítési műveletnek új memóriateret kell igényelnie, és a GC-nek vissza kell szereznie az előző memóriateret is.
Elvelemzés
Ha olvastad az előző blogbejegyzésemet, állítsd be a gyűjteménytípusok kezdeti méretét, és elemezd a C# szótár megvalósítási elvét, tudhatod, hogy a .NET BCL fejlesztők ezeknek az alapvető gyűjteménytípusoknak az alap adatstruktúráit használják arrayk a nagy teljesítményű véletlenszerű hozzáféréshez, vegyük például a <T>Listet.
Hozz létre egy új tömböt a hozzáadott elemek tárolására. Ha nincs elég hely a tömbben, a bővítési művelet elindul, hogy kétszer a tér méretét kérje. A konstruktor kód a következő, és látható, hogy egy általános tömb, amelyet közvetlenül létrehoznak:
Tehát ha memóriát akarsz megosztani, csak azt kell megváltoztatnod, ahol az új kulcsszó alkalmazása az osztálykönyvtárban található, hogy a csoportos alkalmazást használd. Itt osztom meg veled. A NET BCL egy ArrayPool nevű típus, amely egy újrahasználható általános példányokból álló tömberőforrás-készletet biztosít, amelyekkel csökkenthetik a GC terhelését és javíthatják a teljesítményt gyakori tömblétrehozás és megsemmisítés esetén.
A Pooled típusunk mögöttes rétege az ArrayPool erőforrás-poolok megosztására, és a konstruktorból láthatjuk, hogy alapértelmezés szerint az <T>ArrayPool-ot használja. Shared arrayobjektumok hozzárendelésére, és természetesen létrehozhatsz saját ArrayPool-ot is, hogy használd.
Ezen felül, amikor kapacitásigazítási műveletet (bővítést) végzünk, a régi tömb visszakerül a szál poolba, és az új tömböt is beszerezik a poolból.
Ezen felül a szerző a Span-t használja az olyan API-k optimalizálására, mint az Add és Insert, hogy jobb véletlen hozzáférési teljesítményt nyújtson. Ezen felül hozzáadták a TryXXX sorozatú API-t, így kényelmesebb módon tudod használni. Például a Lista <T>osztály akár <T>170 módosítással rendelkezik a PooledListhez képest.
összefoglalás
Valódi online használatunkban a natív gyűjteménytípust a Pooled által biztosított gyűjteménytípusra cserélhetjük, ami nagyon hasznos a memóriahasználat és a P95 késleltetés csökkentésében. Ráadásul, még ha el is felejteed kiadni, a teljesítmény nem lesz sokkal rosszabb, mint a natív gyűjteménytípus használatával. Természetesen a legjobb szokás, ha időben kiengedjük.
Eredeti:A hiperlink bejelentkezés látható.
|