Go-runtime die de rust van een kopje groene thee wegneemt, GreenTea GC
Waarom verschijnt er opnieuw een nieuwe GC?
Waarom de bestaande GC
De huidige Go GC kan worden beschreven met de volgende termen:
- concurrent tri-color mark and sweep
- De status van objecten wordt bijgehouden met behulp van drie kleuren (wit, grijs, zwart).
- Wit: Objecten die nog niet zijn bezocht.
- Grijs: Objecten die zijn bezocht, maar waarvan de onderliggende objecten nog niet allemaal zijn bezocht.
- Zwart: Objecten waarvan alle onderliggende objecten zijn bezocht.
- Na voltooiing van de iteratie worden alle witte objecten verzameld.
- Write Barrier wordt gebruikt om nieuw gecreëerd geheugen te beschermen tijdens GC.
- Het in-/uitschakelen van de Write Barrier vormt een aanzienlijk deel van wat algemeen bekend staat als Stop-The-World (hierna STW).
- Dit proces wordt gelijktijdig uitgevoerd.
- De status van objecten wordt bijgehouden met behulp van drie kleuren (wit, grijs, zwart).
- No Compaction
- Go's GC voert geen compactie uit.
- Geheugenfragmentatie kan optreden.
- Echter, objecten kleiner dan 32kb minimaliseren fragmentatie door gebruik te maken van de per-P cache van de Go language memory Allocator.
- Non-Generational
- Go's GC beheert objecten niet per generatie.
- Alle objecten behoren tot dezelfde generatie.
- Escape Analysis
- Go bepaalt via Escape Analysis of een object wordt toegewezen op de heap of op de stack.
- Grofweg kan worden aangenomen dat objecten op de heap worden toegewezen als ze gebruikmaken van dangling pointers of interfaces.
Het belangrijkste punt
Het belangrijke punt van Go's GC is dat het alle objecten vanuit de root doorzoekt en een driekleurenmarkering uitvoert. Dit proces kan in één zin worden beschreven als een 'gelijktijdig graafdoorloopalgoritme voor topologische sortering'. Echter, het is zeer waarschijnlijk dat individuele objecten zich in verschillende geheugengebieden bevinden. Concreet gesproken:
- Laten we aannemen dat er geheugengebieden zijn die ongeveer 32 MB van elkaar verwijderd zijn.
- In elk van deze twee geheugengebieden zijn 5 objecten toegewezen.
- Objecten A, B, C bevinden zich in gebied 1.
- Objecten D, E bevinden zich in gebied 2.
- De referentievolgorde van de objecten is
A -> D -> B -> E -> C
. - Wanneer de GC start, bezoekt het A, dan D, dan B, dan E, en dan C.
- Aangezien A en D zich in verschillende geheugengebieden bevinden, moet het geheugengebied worden gewijzigd tijdens het bezoeken van A en D.
- Dit proces, inclusief het wijzigen van geheugengebieden en het vullen van nieuwe cachelijnen, brengt zogenaamde geheugensprongkosten met zich mee. Een aanzienlijk deel van de CPU-tijd die de applicatie gebruikt, kan aan dergelijke GC-taken worden toegewezen.
- Deze kosten blijven zich voordoen zolang de GC actief is.
Dus, wat is het probleem?
De werking van deze GC is schadelijk in de volgende gevallen:
- Veel Cores en groot geheugen
- Go's GC wordt in principe gelijktijdig uitgevoerd.
- Maar als het geheugengebied groot is en objecten verspreid zijn, zullen meerdere cores gelijktijdig objecten in verschillende geheugenruimtes doorzoeken.
- Als de bus tussen de CPU en het geheugen in dit proces niet groot genoeg is, kan de geheugenbandbreedte een bottleneck worden.
- Bovendien zal elke afzonderlijke doorzoeking relatief veel vertraging veroorzaken, vanwege de fysieke afstand.
- Veel kleine objecten
- Als u diepe of brede bomen of grafieken in Go gebruikt, moet de GC al deze objecten doorzoeken.
- Als de objecten in dit proces verspreid zijn over verschillende geheugengebieden, ontstaat er vertraging om de eerste reden.
- Bovendien, als er veel kleine objecten zijn, moet de GC al deze objecten doorzoeken, wat betekent dat de CPU-kernen tijdens de uitvoering van de GC aanzienlijk veel capaciteit aan de GC zullen toewijzen en daarvoor zullen werken.
Om deze problemen op te lossen, heeft het Golang-team GreenTea GC aangekondigd.
U kunt zich geen thee meer veroorloven
Wat ontneemt u de thee?
Het gebied dat als de snelste oplossing voor de bestaande GC werd beschouwd, was geheugenlocaliteit. Dat wil zeggen, het minimaliseren van geheugensprongen door objecten dicht bij elkaar te plaatsen. Echter, de patronen van programmeurs kunnen niet worden afgedwongen, en objecttoewijzing is onvoorspelbaar afhankelijk van de workflow.
Daarom heeft het Golang-team gekozen voor Memory Span.
Wat is Memory Span?
Een Memory Span is een relatief groot geheugengebied dat wordt toegewezen om kleine objecten toe te wijzen. En de nieuwe GC, genaamd GreenTea GC, voert garbage collection uit op deze Memory Spans. De gedetailleerde werking is vrijwel identiek aan de bestaande Tri-Color Mark and Sweep.
Werking van GreenTea GC
Eerst wijst GreenTea GC een Memory Span toe. De grootte, zoals eerder beschreven, is 8 KiB, wat een redelijke omvang is. Binnen deze span kunnen objecten met een maximale grootte van 512 bytes worden toegewezen. Dit is precies de grootte van knooppunten in bomen of grafieken zoals in het voorbeeld, of de grootte van structuren die naar de heap ontsnappen en die naar verwachting niet groter zullen zijn dan dit. Objecten kleiner dan 512 bytes worden in deze Memory Span geplaatst telkens wanneer ze naar de heap ontsnappen, en wanneer deze Memory Span vol is, wordt een nieuwe Memory Span toegewezen.
Wanneer nu een GC plaatsvindt, plaatst GreenTea GC deze Memory Spans in een wachtrij en inspecteert ze sequentieel. Tijdens dit proces gebruikt GreenTea GC een planning die vrijwel vergelijkbaar is met het bestaande GMP-model. Ook gebieden zoals Work Stealing zijn geïmplementeerd. Hoe dan ook, een worker die een Memory Span uit de wachtrij haalt, inspecteert de interne objecten van de aan hem toegewezen Memory Span. Tijdens dit proces wordt Tri-Color Mark and Sweep op dezelfde manier gebruikt.
Wat zijn de voordelen hiervan?
Het belangrijkste verschil tussen dit proces en de bestaande GC is slechts één. De eenheid waarmee GC wordt uitgevoerd, is veranderd van object naar Memory Span. Dit geeft de GC de volgende voordelen:
- Geheugenlocaliteit: Omdat objecten zijn verzameld in een Memory Span, worden geheugensprongen geminimaliseerd bij het doorzoeken van objecten. Dat wil zeggen, de CPU-cache kan optimaal worden benut.
- Verbeterde GC-prestaties: Door GC uit te voeren op basis van Memory Spans, werkt de GC efficiënter in een multi-core omgeving.
- Geheugentoewijzingsoptimalisatie: Memory Spans worden toegewezen met een vaste grootte, waardoor de overhead van geheugentoewijzing wordt verminderd bij het toewijzen van kleine objecten, en de kans op fragmentatie afneemt. Dit verhoogt de efficiëntie van geheugenvrijgave.
Kortom, vanaf nu kunnen we vaker en met een gerust hart objecten kleiner dan 512 bytes toewijzen.
Er zijn echter ook gunstige scenario's:
- Boom-/graafstructuren: wanneer de fan-out hoog is en er weinig wijzigingen optreden
- B+ boom, Trie, AST (Abstract Syntax Tree)
- Cache-vriendelijke datastructuren
- Batchverwerking: workflow voor massale toewijzing van kleine gegevens
- Meerdere kleine objecten gegenereerd door JSON-parsing
- DAO-objecten van databasetabellen
In deze gevallen kan GreenTea GC betere prestaties leveren dan de bestaande GC. Dit geldt echter niet voor alle gevallen, en met name wanneer objecten verspreid zijn over verschillende geheugengebieden, is een dramatische prestatieverbetering nog steeds moeilijk te verwachten.
Dus
Het Golang-team lijkt van plan te zijn om de GC op de lange termijn te blijven verbeteren. Deze GreenTea GC kan worden gezien als een kleine wijziging in een klein gebied en vervangt de bestaande GC niet. Echter, GreenTea GC lijkt een interessant voorbeeld te zijn dat een glimp opvangt van de problemen waarmee het Golang-team wordt geconfronteerd, of die het verwacht. Het was ook indrukwekkend dat ze een relatief complex probleem probeerden op te lossen met de toevoeging van een relatief eenvoudig concept. Persoonlijk vind ik het een interessant geval.
GreenTea GC is een experimentele functie die vanaf Go 1.25 wordt geïntroduceerd. Het kan worden geactiveerd door de omgevingsvariabele GOEXPERIMENT=greenteagc
te gebruiken. Deze functie is nog experimenteel, dus voldoende testen is vereist voordat u deze in een productieomgeving gebruikt.