
De vertaling van het artikel is voorbereid voor studenten van de cursus .
Eerder heb ik besproken hoe je Hugepages in Linux kunt controleren en inschakelen.
Dit artikel is alleen nuttig als je daadwerkelijk een toepassing hebt voor Hugepages. Ik heb veel mensen ontmoet die zich laten misleiden door de gedachte dat Hugepages op magische wijze de prestaties verbeteren. Toch is hugepaging een complex onderwerp en kan het, bij verkeerd gebruik, de prestaties verlagen.
Deel 1: controleren of hugepages zijn ingeschakeld in Linux (origineel )
Probleem:
Je moet controleren of HugePages op je systeem zijn ingeschakeld.
Oplossing:
Het is vrij eenvoudig:
cat /sys/kernel/mm/transparent_hugepage/enabledJe krijgt iets als dit:
altijd [madvise] nooitJe zult een lijst met beschikbare opties zien (altijd, madvise, nooit), waarbij de huidige actieve optie tussen haakjes staat (standaard madvise).
madvise betekent dat transparante hugepages zijn alleen ingeschakeld voor geheugengebieden die expliciet hugepages aanvragen via .
altijd betekent dat transparante hugepages worden altijd ingeschakeld voor alle processen. Dit verbetert meestal de prestaties, maar als je een gebruikssituatie hebt waarin veel processen een kleine hoeveelheid geheugen gebruiken, kan de totale geheugendruk aanzienlijk toenemen.
nooit betekent dat transparante hugepages wordt zelfs niet ingeschakeld wanneer het wordt aangevraagd via madvise. Voor meer informatie, zie de Linux-kernel.
Hoe de standaardwaarde te wijzigen
Optie 1: Direct de wijziging aanbrengen sysfs (na herstart keert de parameter terug naar de standaardwaarde):
echo altijd > /sys/kernel/mm/transparent_hugepage/enabled
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo nooit > /sys/kernel/mm/transparent_hugepage/enabledOptie 2: Wijzig de systeemstandaardwaarde door de kernel opnieuw te compileren met een gewijzigde configuratie (deze optie wordt alleen aanbevolen als je een eigen kernel gebruikt):
- Om altijd standaard in te stellen, gebruik:
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Commentaar uit CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y - Om madvise standaard in te stellen, gebruik:
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Commentaar uit CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
Deel 2: Voordelen en nadelen van HugePages
We zullen proberen de voordelen, nadelen en mogelijke fouten bij het gebruik van Hugepages selectief uit te leggen. Aangezien dit een technologisch gecompliceerd en pedant artikel is, zal het waarschijnlijk moeilijk te begrijpen zijn voor mensen die zich laten misleiden door Hugepages als een wondermiddel. Ik zal nauwkeurigheid opofferen voor eenvoud. Het is gewoon belangrijk om te onthouden dat veel onderwerpen echt ingewikkeld zijn en daarom sterk vereenvoudigd worden.
Let op dat we het hebben over 64-bits x86 systemen die draaien op Linux, en dat ik gewoon aanneem dat het systeem transparent hugepages ondersteunt (omdat het geen tekortkoming is dat hugepages niet kunnen worden ingeschakeld), zoals dat vrijwel altijd het geval is in moderne Linux-omgevingen.
In de onderstaande links voeg ik meer technische beschrijvingen toe.
Virtueel geheugen
Als je een C++ programmeur bent, weet je dat objecten in het geheugen specifieke adressen hebben (pointerwaarden).
Deze adressen weerspiegelen echter niet noodzakelijk de fysieke adressen in het geheugen (adressen in het RAM). Het zijn adressen in het virtuele geheugen. De processor heeft een speciale module, de MMU (memory management unit), die het besturingssysteem helpt om virtueel geheugen in te passen bij fysieke locaties.
Deze benadering heeft veel voordelen, maar de belangrijkste zijn:
- Prestaties (om verschillende redenen);
- Isolatie van programma's, wat betekent dat geen enkel programma kan lezen uit het geheugen van een ander programma.
Wat zijn pagina's?
Virtueel geheugen is verdeeld in pagina's. Elke afzonderlijke pagina wijst naar een bepaald fysiek geheugen; het kan verwijzen naar een gebied in het RAM of naar een adres dat is toegewezen aan een fysiek apparaat, zoals een grafische kaart.
De meeste pagina's waarmee je te maken hebt, wijzen ofwel naar RAM of worden gewisseld (swap), dat wil zeggen opgeslagen op een harde schijf of SSD. De kernel beheert de fysieke locatie van elke pagina. Als er toegang wordt gevraagd tot een gewisselde pagina, stopt de kernel de thread die probeert toegang te krijgen tot het geheugen, leest de pagina van de harde schijf/SSD in het RAM, en gaat dan verder met de uitvoering van de thread.
Dit proces is transparant voor de thread, dat wil zeggen dat deze niet rechtstreeks leest van de harde schijf/SSD. De grootte van standaard pagina's is 4096 bytes. De grootte van Hugepages is 2 megabyte.
Translation Lookaside Buffer (TLB)
Wanneer een programma toegang vraagt tot een bepaalde geheugenpagina, moet de centrale processor weten van welke fysieke pagina de gegevens moeten worden gelezen (d.w.z. moet het een virtuele adressenkaart hebben).
In de kernel is er een datastructuur (pagina-tabellen) die alle informatie over de gebruikte pagina's bevat. Met behulp van deze datastructuur kan een virtueel adres worden gekoppeld aan een fysiek adres.
De paginatabel is echter vrij complex en werkt traag, waardoor we niet elke keer de hele datastructuur kunnen analyseren wanneer een proces geheugen aanspreekt.
Gelukkig hebben we in onze processor een TLB die de mapping van virtuele naar fysieke adressen cachet. Dit betekent dat, hoewel we de paginatabel bij de eerste toegang moeten analyseren, alle daaropvolgende pagina-aanroepen via de TLB kunnen worden afgehandeld, wat zorgt voor snelle prestaties.
Aangezien het is geïmplementeerd als een fysiek apparaat (wat het in de eerste plaats snel maakt), is de capaciteit ervan beperkt. Als je dus toegang wilt tot meer pagina's, kan de TLB niet de mapping voor allemaal opslaan, waardoor je programma veel langzamer wordt.
Hugepages komen ter hulp.
Wat kunnen we doen om TLB-overloop te voorkomen? (We nemen aan dat de programma nog steeds dezelfde hoeveelheid geheugen nodig heeft).
Hier komen Hugepages in beeld. In plaats van 4096 bytes, waarvoor slechts één vermelding in de TLB nodig is, kan één vermelding in de TLB nu wijzen naar maar liefst 2 megabyte. Laten we aannemen dat de TLB 512 vermeldingen heeft; hier kunnen we zonder Hugepages toewijzen:
4096 b⋅512=2 MBTerwijl we met hen kunnen toewijzen:
2 MB⋅512=1 GBDaarom zijn Hugepages geweldig. Ze kunnen de prestaties verbeteren zonder veel moeite. Maar er zijn belangrijke kanttekeningen.
Vervanging van Hugepages
De kernel houdt automatisch bij hoe vaak elke geheugensectie wordt gebruikt. Als er niet genoeg fysiek geheugen (RAM) is, zal de kernel minder belangrijke (minder vaak gebruikte) pagina's naar de harde schijf verplaatsen om wat RAM vrij te maken voor belangrijkere pagina's.
In principe geldt hetzelfde voor Hugepages. De kernel kan echter alleen hele pagina's verplaatsen en niet individuele bytes.
Stel dat we zo'n programma hebben:
char* mymemory = malloc(2*1024*1024); // Laten we dit beschouwen als één Hugepage!
// Vul mymemory met enkele gegevens
// Voer veel andere dingen uit,
// die zullen leiden tot vervanging van de mymemory-pagina
// ...
// Vraag alleen toegang tot de eerste byte
putchar(mymemory[0]); In dit geval moet de kernel 2 megabyte aan informatie van de harde schijf/SSD lezen, alleen om één byte te kunnen lezen. Wat gewone pagina's betreft, hoeft er slechts 4096 bytes van de harde schijf/SSD te worden gelezen.
Daarom gaat het lezen van hugepages sneller als het wordt vervangen, maar alleen als je toegang nodig hebt tot de hele pagina. Dit betekent dat als je willekeurig verschillende delen van het geheugen probeert te benaderen en slechts een paar kilobyte leest, je gewone pagina's moet gebruiken en je om niets anders hoeft te bekommeren.
Aan de andere kant, als je toegang moet krijgen tot een groot deel van het geheugen op een consequente manier, zullen hugepages je prestaties verbeteren. Je moet dit zelf verifiëren (en niet met behulp van abstracte software) en kijken wat sneller werkt.
Geheugenallocatie
Als je in C schrijft, weet je dat je zoveel kleine (of bijna zoveel grote) hoeveelheden geheugen uit de heap kunt aanvragen als je wilt met malloc(). Stel dat je 30 bytes geheugen nodig hebt:
char* mymemory = malloc(30);Voor de programmeur lijkt het alsof je 30 bytes geheugen van het besturingssysteem 'aanvraagt' en een pointer terugkrijgt naar een bepaald virtueel geheugen. Maar in werkelijkheid malloc () is gewoon een C-functie die intern de functies aanroept om geheugen aan te vragen of vrij te geven van het besturingssysteem.
Echter, steeds meer geheugen aanvragen voor elke allocatie is inefficiënt; het is zeer waarschijnlijk dat een bepaald geheugensegment al is vrijgegeven (free()), en we kunnen het hergebruiken. malloc() Dit implementeert behoorlijk complexe algoritmes voor het hergebruiken van vrijgegeven geheugen.
Voor jou gebeurt dit allemaal ongemerkt, waarom zou je je daar druk om maken? Omdat het aanroepen van free() niet betekent dat .
Er bestaat een fenomeen genaamd geheugenfragmentatie. In extreme gevallen zijn er heap-segmenten waar slechts een paar bytes worden gebruikt, terwijl alles daartussen is vrijgegeven. (free()).
Houd er rekening mee dat geheugenfragmentatie een ongelooflijk complex onderwerp is en dat zelfs kleine wijzigingen in een programma er een aanzienlijke invloed op kunnen hebben. In de meeste gevallen veroorzaken programma's geen aanzienlijke geheugenfragmentatie, maar u moet zich ervan bewust zijn dat als er een probleem is met fragmentatie in een bepaald gebied van de heap, hugepages dit alleen maar kunnen verergeren.
Selectief toepassen van hugepages
Na het lezen van het artikel heeft u bepaald welke delen van uw programma kunnen profiteren van het gebruik van hugepages en welke niet. Moet u hugepages überhaupt inschakelen?
Gelukkig kunt u gebruiken madvise(), om hugepaging alleen in te schakelen voor die gebieden van het geheugen waar het nuttig zal zijn.
Begin met te controleren of hugepages werken in modus madvise(), met behulp van aan het begin van het artikel.
Gebruik vervolgens madvise(), om de kernel aan te geven waar precies hugepages moeten worden gebruikt.
#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)Houd er rekening mee dat deze methode slechts aanbevelingen aan de kernel geeft voor het geheugenbeheer. Dit betekent niet dat de kernel automatisch hugepages zal gebruiken voor het aangegeven geheugen.
Raadpleeg de documentatie , om meer te leren over geheugenbeheer en madvise(), dit onderwerp heeft een ongelooflijk steile leercurve. Dus als u echt goed wilt begrijpen hoe het werkt, bereid u dan voor om enkele weken te lezen en te testen voordat u op enige positieve resultaten rekent.
Wat te lezen?
Heeft u een vraag? Schrijf een reactie!
Bron: habr.com
