Go futásidejű környezet által elvett, egy csésze zöld tea nyugalma, a GreenTea GC
Miért van szükség új GC-re?
A létező GC-k miért
Jelenleg a Go GC-je a következőképpen írható le:
- concurrent tri-color mark and sweep
- Három színt (fehér, szürke, fekete) használ az objektumok állapotának nyomon követésére.
- Fehér: Még nem látogatott objektum
- Szürke: Látogatott, de még nem az összes gyermekobjektumát látogatta meg
- Fekete: Az összes gyermekobjektumát meglátogatta
- A bejárás után az összes fehér objektum begyűjtésre kerül.
- Write Barrier-t használ a GC során újonnan létrehozott memória védelmére.
- A Write Barrier be- és kikapcsolásának ideje az, amit gyakran Stop-The-World (a továbbiakban STW) jelentős részének neveznek.
- Ezt a folyamatot konkurensen hajtja végre.
- Három színt (fehér, szürke, fekete) használ az objektumok állapotának nyomon követésére.
- No Compaction
- A Go GC nem végez tömörítést.
- Memóriafragmentáció léphet fel.
- Azonban a 32 kb alatti objektumok a Go nyelv memóriafoglalójának (Allocator) per-P cache-ét használják a fragmentáció minimalizálására.
- Non-Generational
- A Go GC nem kezeli az objektumokat generációk szerint.
- Minden objektum ugyanahhoz a generációhoz tartozik.
- Escape Analysis
- A Go az Escape Analysis segítségével dönti el, hogy az objektumok a heap-en vagy a stack-en kerülnek-e allokálásra.
- Nagyjából, ha dangling pointereket vagy interface-eket használunk, akkor feltételezhető, hogy a heap-en kerülnek allokálásra.
Ami fontos
A Go GC-je az, hogy minden objektumot a gyökértől kezdve bejár, és háromszínű jelölést végez. Ez a folyamat egyetlen sorban leírva egy topológiai rendezésű gráf konkurens bejáró algoritmusa
. Azonban az egyes objektumok nagy valószínűséggel különböző memória területeken helyezkednek el. Egyszerűen fogalmazva:
- Tegyük fel, hogy két memória terület van, amelyek körülbelül 32 MB távolságra vannak egymástól.
- Ezen két memória területen 5 objektum van allokálva.
- Az A, B, C objektumok az 1. területen
- A D, E objektumok a 2. területen
- Az objektumok hivatkozási sorrendje
A -> D -> B -> E -> C
. - Amikor a GC elindul, meglátogatja az A-t, majd a D-t, majd a B-t, majd az E-t, majd a C-t.
- Ekkor, mivel az A és a D különböző memória területeken helyezkednek el, az A látogatását követően a D látogatásához a memória területet váltani kell.
- Ez a folyamat úgynevezett memória ugrási költséget von maga után, mint például a memória terület váltása és új cache line-ok feltöltése. Az alkalmazás által felhasznált CPU idő jelentős része ilyen GC műveletekre fordítódhat.
- Ez a költség a GC futása alatt folyamatosan fennáll.
Szóval mi a probléma?
Ez a GC-működés a következő esetekben problémás:
- Sok mag és nagy memória esetén
- A Go GC alapvetően konkurensen fut.
- Azonban, ha a memória területe széles és az objektumok elosztottak, több mag konkurensen fog objektumokat keresni különböző memóriaterületeken.
- Ebben a folyamatban, ha a CPU és a memória közötti busz nem elég nagy, a memória sávszélesség szűk keresztmetszetté válhat.
- Továbbá, mivel fizikailag távolság van, minden egyes keresés viszonylag nagy késedelmet okoz.
- Sok apró objektum esetén
- Ha mély, vagy sok gyermeket tartalmazó fát vagy gráfot üzemeltet Go-ban, a GC-nek az összes objektumot meg kell vizsgálnia.
- Ebben a folyamatban, ha az objektumok különböző memóriaterületeken oszlanak el, az első ok miatt késleltetés lép fel.
- Továbbá, ha sok apró objektum van, a GC-nek az összes objektumot meg kell vizsgálnia, ezért a GC futása alatt a CPU magok jelentős kapacitást fognak a GC-re fordítani és dolgozni fognak.
Ezen problémák megoldására a Golang csapat bejelentette a GreenTea GC-t.
Többé nincs ideje teát inni
Ami elveszi a teát
A létező GC-k leggyorsabb megoldásaként a memóriahozzárendelést azonosították. Vagyis az objektumok egymáshoz közel helyezkednek el, minimalizálva a memória ugrásokat. Azonban a kódot író programozó mintái nem kényszeríthetők, és az objektumok allokálása a munkafolyamattól függően kiszámíthatatlan.
Ezért a Golang csapat a memória span-t (Memory Span) választotta.
Mi az a Memory Span?
A memória span egy viszonylag nagy memóriaterületet foglal le, és azon belül allokál kis objektumokat. Az új, GreenTea GC-nek nevezett GC pedig ezen memória span-eken végez szemétgyűjtést. A részletes működés szinte teljesen megegyezik a meglévő Tri-Color Mark and Sweep eljárással.
A GreenTea GC működése
Először is, a GreenTea GC allokál egy memória span-t. Ahogy korábban említettük, a mérete viszonylag nagy, 8 KiB. Ezen belül legfeljebb 512 bájt méretű objektumokat lehet allokálni. Ez éppen akkora méret, mint a példaként említett fa vagy gráf csomópontjainak mérete, vagy egy általános heap-re szökő struktúra mérete, ami ennél nagyobb aligha lehet. Az 512 bájt alatti objektumok minden egyes heap-re szökéskor ebbe a memória span-be kerülnek, és amikor ez a memória span megtelik, egy új memória span kerül allokálásra.
Amikor GC történik, a GreenTea GC sorba állítja ezeket a memória span-eket, és szekvenciálisan ellenőrzi őket. Ebben a folyamatban a GreenTea GC szinte azonos ütemezést használ, mint a meglévő GMP modell. A munka lopása (work stealing) is megvalósításra került. Mindenesetre, a sorból egy memória span-t kiváltó worker ellenőrzi a hozzá rendelt memória span belső objektumait. Ebben a folyamatban a Tri-Color Mark and Sweep azonos módon kerül felhasználásra.
Milyen előnyökkel jár ez?
Ebben a folyamatban a meglévő GC-től való különbség lényegében egyetlen dologban rejlik. A GC végrehajtásának egysége objektumról memória span-re változott. Ennek eredményeként a GC a következő előnyökkel jár:
- Memóriahozzárendelés: Mivel az objektumok a memória span-ben gyűlnek össze, az objektumok keresésekor a memória ugrások minimalizálódnak. Ez azt jelenti, hogy a CPU cache-ét a lehető legjobban kihasználhatja.
- GC teljesítmény javulása: A GC memória span egységenkénti végrehajtásával a GC hatékonyabban működik többmagos környezetben.
- Memóriaallokáció optimalizálása: Mivel a memória span-ek fix méretben kerülnek allokálásra, a kis objektumok allokálásakor csökken a memóriaallokáció overhead-je, és csökken a fragmentáció valószínűsége. Ez növeli a memória deallokáció hatékonyságát.
Röviden, mostantól gyakrabban és könnyebb szívvel allokálhatunk 512 bájt alatti objektumokat.
Azonban itt is vannak bizonyos előnyös esetek.
- Fa/gráf struktúrák: nagy fan-out és ritka változások esetén
- B+ fa, Trie, AST (Abstract Syntax Tree)
- Cache-barát adatstruktúrák
- Kötegelt feldolgozás: kis adatok tömeges allokációs munkafolyamata
- JSON-elemzésből létrejövő számos apró objektum
- Adatbázis eredményhalmaz DAO objektumai
Ezekben az esetekben a GreenTea GC jobb teljesítményt nyújthat, mint a meglévő GC. Azonban nem minden esetben alkalmazható, és különösen akkor, ha az objektumok különböző memóriaterületeken oszlanak el, továbbra is nehéz drámai teljesítménynövekedést várni.
Összefoglalva
A Golang csapat valószínűleg hosszú távon is fejleszteni kívánja a GC-t. Ez a GreenTea GC egy kisebb változtatásnak tekinthető egy kis területen, és nem helyettesíti a meglévő GC-t. Azonban a GreenTea GC érdekes példa arra, hogy a Golang csapat milyen problémákkal szembesül, vagy milyen problémákat vár. Meglepően összetett probléma, de a viszonylag egyszerű koncepció hozzáadása révén történő megoldási kísérlet is lenyűgöző volt. Személy szerint érdekes esetnek tartom.
A GreenTea GC egy kísérleti funkció, amelyet a Go 1.25-től vezetnek be. Az GOEXPERIMENT=greenteagc
környezeti változó használatával aktiválható és használható. Ez a funkció még kísérleti, ezért alapos tesztelésre van szükség, mielőtt éles környezetben használnák.