GreenTea GC: En rolig kopp grønn te fra Go Runtime
Hvorfor en ny GC igjen?
Hvorfor den eksisterende GC?
Den nåværende Go GC kan beskrives med følgende termer:
- concurrent tri-color mark and sweep
- Den bruker totalt tre farger (hvit, grå, svart) for å spore tilstanden til objekter.
- Hvit: Objekter som ennå ikke er besøkt
- Grå: Objekter som er besøkt, men hvis alle underordnede objekter ikke er besøkt
- Svart: Objekter hvis alle underordnede objekter er besøkt
- Etter fullført traversering samles alle hvite objekter inn.
- Den bruker en Write Barrier for å beskytte nyallokert minne under GC-prosessen.
- Tiden det tar å slå Write Barrier av/på utgjør en betydelig del av det som ofte kalles Stop-The-World (heretter STW).
- Denne prosessen utføres samtidig.
- Den bruker totalt tre farger (hvit, grå, svart) for å spore tilstanden til objekter.
- No Compaction
- Go's GC utfører ikke komprimering.
- Dette kan føre til minnefragmentering.
- Imidlertid minimeres fragmentering for objekter under 32kb ved å utnytte Go-språkets minneallokator (Allocator) sin per-P cache.
- Non-Generational
- Go's GC administrerer ikke objekter basert på generasjoner.
- Alle objekter tilhører samme generasjon.
- Escape Analysis
- Go bruker Escape Analysis for å bestemme om et objekt skal allokeres på heapen eller stacken.
- Grovt sett kan man anta at et objekt allokeres på heapen hvis det bruker dangling pointers eller interfaces.
Det viktigste er
Go's GC utfører trefarget markering ved å traversere alle objekter fra roten. Denne prosessen kan i én setning beskrives som en "samtidig traverseringsalgoritme for topologisk sorterte grafer". Imidlertid er det sannsynlig at hvert objekt eksisterer i forskjellige minneområder. For å illustrere:
- Anta at det finnes minneområder som ligger omtrent 32 MB fra hverandre.
- Fem objekter er allokert i hvert av disse to minneområdene.
- Objektene A, B, C i område 1
- Objektene D, E i område 2
- Objektreferanserekkefølgen er
A -> D -> B -> E -> C
. - Når GC starter, besøker den A, deretter D, deretter B, deretter E, og til slutt C.
- I dette tilfellet, siden A og D eksisterer i forskjellige minneområder, må minneområdet flyttes under prosessen med å besøke A og deretter D.
- Denne prosessen innebærer en såkalt "minnehoppkostnad", som inkluderer flytting av minneområder og fylling av nye cachelinjer. En betydelig del av CPU-tiden som applikasjonen bruker, kan allokeres til slike GC-operasjoner.
- Denne kostnaden oppstår kontinuerlig mens GC pågår.
Så hva er problemet?
Denne GC-atferden er svært ugunstig i følgende tilfeller:
- Når det er mange kjerner og mye minne
- Go's GC utføres i utgangspunktet samtidig.
- Men hvis minneområdet er stort og objekter er spredt, vil flere kjerner samtidig søke etter objekter i ulike minneområder.
- I denne prosessen, hvis bussen mellom CPU og minne ikke er tilstrekkelig stor, kan minnebåndbredden bli en flaskehals.
- Dessuten, på grunn av den fysiske avstanden, vil hver enkelt søk ta relativt lang tid.
- Når det er mange små objekter
- Hvis du opererer trær eller grafer med dyp dybde eller mange barn i Go, må GC traversere alle disse objektene.
- I denne prosessen, hvis objektene er spredt over forskjellige minneområder, oppstår det en forsinkelse på grunn av den første årsaken.
- I tillegg, hvis det er mange små objekter, må GC traversere alle disse objektene, noe som betyr at CPU-kjernene vil allokere en betydelig mengde kapasitet til GC og jobbe under GC-utførelsen.
For å løse disse problemene har Golang-teamet annonsert GreenTea GC.
Du har ikke råd til mer grønn te
Hva tar fra deg grønn te?
Det området som ble ansett som mest egnet for å anvende den raskeste løsningen for eksisterende GC, var minnelokalitet. Det vil si å minimere minnesprang ved å plassere objekter nær hverandre. Imidlertid kan man ikke tvinge programmererens mønstre ved kodeskriving, og objektallokering er uforutsigbar avhengig av arbeidsflyten.
Derfor har Golang-teamet valgt Memory Span.
Hva er Memory Span?
Memory Span er et sted der et relativt stort minneområde allokeres for å allokere små objekter. Og den nye GC kalt GreenTea GC utfører søppelinnsamling på denne Memory Span. Den detaljerte operasjonen er nesten identisk med den eksisterende Tri-Color Mark and Sweep.
GreenTea GCs funksjon
Først allokerer GreenTea GC en Memory Span. Størrelsen er, som tidligere beskrevet, 8 KiB, som er en viss størrelse. Og innenfor denne kan objekter med en maksimal størrelse på 512 bytes allokeres. Dette er omtrent størrelsen på noder i trær eller grafer som ble brukt som eksempler, eller størrelsen på generelle strukturer som slipper ut til heapen, som det er vanskelig å forestille seg større enn dette. Objekter under 512 bytes akkumuleres i denne Memory Span hver gang de slipper ut til heapen, og når denne Memory Span er full, allokeres en ny Memory Span.
Når GC oppstår, legger GreenTea GC disse Memory Spans i en kø og inspiserer dem sekvensielt. I denne prosessen bruker GreenTea GC en planlegging som er nesten identisk med den eksisterende GMP-modellen. Arbeidstyveri er også implementert. Uansett, en arbeider som tar ut en Memory Span fra køen, inspiserer de interne objektene i Memory Span som er tildelt den. I denne prosessen brukes Tri-Color Mark and Sweep identisk.
Hva er fordelen med dette?
Den største forskjellen mellom denne prosessen og den eksisterende GC er i hovedsak bare én. Enheten som GC utføres på, har endret seg fra objekt til minnespan. Som et resultat har GC følgende fordeler:
- Minnelokalitet: Siden objekter er samlet i minnespanner, minimeres minnesprang når objekter traverseres. Det vil si at CPU-cachen kan utnyttes maksimalt.
- Forbedret GC-ytelse: Ved å utføre GC på minnespan-nivå, fungerer GC mer effektivt i et flerkjernemiljø.
- Minneallokeringsoptimalisering: Minnespanner allokeres med fast størrelse, noe som reduserer overhead for minneallokering ved allokering av små objekter, og reduserer sannsynligheten for fragmentering. Dette øker effektiviteten av minnefrigjøring.
Kort sagt, fra nå av kan vi allokere objekter under 512 bytes oftere og med lettere hjerte.
Det er imidlertid visse gunstige scenarier her også.
- Tre-/grafstruktur: Når fan-out er høy og endringer sjelden forekommer
- B+ tre, Trie, AST (Abstract Syntax Tree)
- Cache-vennlige datastrukturer
- Batchbehandling: Arbeidsflyter med stor allokering av små data
- Mange små objekter generert av JSON-parsing
- DAO-objekter fra databaserresultatsett
I slike tilfeller kan GreenTea GC oppnå bedre ytelse enn eksisterende GC. Dette gjelder imidlertid ikke i alle tilfeller, og spesielt når objekter er spredt over forskjellige minneområder, er det fortsatt vanskelig å forvente en dramatisk ytelsesforbedring.
Derfor
Golang-teamet ser ut til å ha til hensikt å fortsette å forbedre GC på lang sikt. Denne GreenTea GC kan sees på som en mindre endring i et lite område, og den erstatter ikke den eksisterende GC. Imidlertid virker GreenTea GC som et interessant tilfelle som gir et innblikk i problemene Golang-teamet står overfor, eller forventer. Selv om det er et komplekst problem, var forsøket på å løse det med et relativt enkelt konsept også imponerende. Personlig synes jeg det er et interessant tilfelle.
GreenTea GC er en eksperimentell funksjon som introduseres fra Go 1.25. Den kan aktiveres ved å bruke miljøvariabelen GOEXPERIMENT=greenteagc
. Denne funksjonen er fortsatt eksperimentell, så tilstrekkelig testing er nødvendig før den tas i bruk i produksjonsmiljøer.