
Prevod članka pripremljen za studente kursa .
Prej sem predstavil, kako preveriti in omogočiti uporabo Hugepages v Linuxu.
Ta članek bo koristen le, če imate resnično potrebo po uporabi Hugepages. Srečal sem veliko ljudi, ki se zavedajo perspektive, da bodo Hugepages čudežno izboljšali zmogljivost. Kljub temu je hugepaging zapletena tema in ob napačni uporabi lahko zmanjša zmogljivost.
Del 1: preverimo, ali so hugepages omogočeni v Linuxu (izvirnik )
Težava:
Potrebno je preveriti, ali so HugePages omogočeni v vašem sistemu.
Rešenje:
To je precej preprosto:
cat /sys/kernel/mm/transparent_hugepage/enabledPrejeli boste nekaj takega:
always [madvise] neverVideli boste seznam razpoložljivih možnosti (always, madvise, never), pri čemer bo trenutno aktivna možnost označena s oklepaji (privzeto madvise).
madvise pomeni, da transparent hugepages so omogočeni samo za spominske območja, ki izrecno zahtevajo hugepages s pomočjo .
always pomeni, da transparent hugepages so vedno omogočeni za vse procese. Običajno to poveča zmogljivost, vendar, če imate možnost uporabe, kjer več procesov uporablja majhno količino pomnilnika, lahko splošna obremenitev pomnilnika dramatično naraste.
never pomeni, da transparent hugepages ne bodo omogočeni niti ob zahtevi z uporabo madvise. Za več informacij se obrnite na jedra Linux.
Kako spremeniti privzeto vrednost
Možnost 1: Neposredno spremenite sysfs (po ponovni spremembi se parametri vrnejo na privzeto vrednost):
echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabledMožnost 2: Spremenite sistemsko privzeto vrednost s ponovnim sestavljanjem jedra s spremenjeno konfiguracijo (ta možnost se priporoča le, če uporabljate lastno jedro):
- Za nastavitev always kot privzeto uporabite:
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Komentirajte CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y - Za nastavitev madvise kot privzeto uporabite:
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Komentirajte CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
Del 2: Prednosti in slabosti HugePages
Poskušali bomo selektivno razložiti prednosti, slabosti in morebitne napake pri uporabi Hugepages. Ker je tehnološko zahtevna in pedantna tema, bo verjetno težko razumljiva ljudem, ki se varajo in menijo, da so Hugepages panaceja, žrtvujem natančnost v korist preprostosti. Vredno je imeti v mislih, da so mnoge teme resnično zapletene in zato močno poenostavljene.
Ugotovite, da govorimo o 64-bitnih x86 sistemih, ki delujejo na Linuxu, in da preprosto predvidevam, da sistem podpira transparent hugepages (saj ne more biti pomanjkljivost, če hugepages niso zamenjani), kar se zgodi skoraj v vsakem sodobnem okolju Linux.
V spodnjih povezavah bom priložil več tehničnih opisov.
Virtualna pomnilnika
Če ste programer C++, veste, da imajo objekti v pomnilniku specifične naslove (vrednosti kazalca).
Vendar ti naslovi morda ne odražajo fizičnih naslovov v pomnilniku (naslovov v RAM-u). Predstavljajo naslove v virtualni pomnilniku. Procesor ima poseben modul MMU (enota za upravljanje pomnilnika), ki pomaga jedru, da poveže virtualni pomnilnik s fizičnim mestom.
Takšen pristop ima številne prednosti, vendar so najosnovnejše med njimi:
- Učinkovitost (iz različnih razlogov);
- Izolacija programov, torej noben program ne more brati iz pomnilnika drugega programa.
Kaj so strani?
Virtualna pomnilnika je razdeljena na strani. Vsaka posamezna stran kaže na določeno fizično pomnilnik, lahko kaže na območje v RAM-u ali na naslov, dodeljen fizičnemu napravi, na primer grafični kartici.
Večina strani, s katerimi imate opravka, kaže ali na RAM ali se zamenjuje (swap), torej shranjujejo se na trdem disku ali SSD-ju. Jedro upravlja s fizično lokacijo vsake strani. Če dostopate do zamenjane strani, jedro ustavi tok, ki poskuša dostopati do pomnilnika, prebere stran s trdega diska/SSD-jež v RAM, nato pa nadaljuje z izvajanjem toka.
Ta postopek je za tok prozoren, torej ne bere nujno neposredno s trdega diska/SSD-ja. Velikost običajnih strani je 4096 bajtov. Velikost Hugepages je 2 megabajta.
Predpomnilnik asociativne transformacije (TLB)
Ko program dostopa do določene strani pomnilnika, mora centralni procesor vedeti, s katere fizične strani prebrati podatke (tj. imeti virtualno karto naslovov).
V jedru obstaja podatkovna struktura (tabela strani), ki vsebuje vse informacije o uporabljenih straneh. S to podatkovno strukturo lahko povežete virtualni naslov s fizičnim naslovom.
Vendar je tabela strani precej zapletena in deluje počasi, zato ne moremo vsakič analizirati celotne podatkovne strukture, ko proces dostopa do pomnilnika.
Na srečo ima naš procesor TLB, ki predpomni preslikavo virtualnih in fizičnih naslovov. To pomeni, da čeprav moramo pri prvem dostopu analizirati tabelo strani, lahko vse nadaljnje zahteve za stran obdelamo v TLB, kar zagotavlja hitro delovanje.
Ker je TLB implementiran kot fizična naprava (kar zagotavlja hitro delovanje), je njegova zmogljivost omejena. Zato, če želite dostopati do več strani, TLB ne bo mogel shraniti preslikave za vse njih, zaradi česar bo vaša programska oprema delovala veliko počasneje.
Hugepages prihajajo na pomoč.
Kaj lahko storimo, da se izognemo preobremenitvi TLB? (Predpostavljamo, da program še vedno potrebuje enako količino pomnilnika).
Tukaj pridejo Hugepages v igro. Namesto 4096 bajtov, ki zahtevajo le en zapis v TLB, lahko zdaj en zapis v TLB kaže na ogromnih 2 megabajta. Predpostavimo, da ima TLB 512 zapisov, brez Hugepages lahko preslikamo:
4096 b⋅512=2 MBMedtem ko s Hugepages lahko preslikamo:
2 MB⋅512=1 GBZato so Hugepages odlične. Lahko izboljšajo zmogljivost brez večjih naporov. Vendar pa so tukaj pomembne omejitve.
Zamenjava Hugepages
Jedro samodejno sledi pogostosti uporabe vsake strani pomnilnika. Če fizičnega pomnilnika (RAM) primanjkuje, bo jedro premestilo manj pomembne (redkeje uporabljene) strani na trdi disk, da sprosti del RAM-a za pomembnejše strani.
Na splošno velja isto za Hugepages. Vendar pa lahko jedro zamenja le celotne strani, ne pa posameznih bajtov.
Predpostavimo, da imamo takšno programsko opremo:
char* mymemory = malloc(2*1024*1024); // Vzemimo to za en Hugepage!
// Napolnimo mymemory z nekaterimi podatki
// Naredimo še veliko drugih stvari,
// kar bo pripeljalo do zamenjave strani mymemory
// ...
// Zahtevamo dostop samo do prvega bajta
putchar(mymemory[0]); V tem primeru bo jedru potrebno prebrati kar 2 megabajta informacij s trdega diska/SSD, samo da preberete en bajt. Kar se tiče običajnih strani, je potrebno prebrati le 4096 bajtov s trdega diska/SSD.
Zato, če se hugepage zamenja, se njeno branje zgodi hitreje, le če morate dostopati do celotne strani. To pomeni, da, če poskušate dostopati naključno do različnih delov pomnilnika in preberete samo nekaj kilobajtov, bi morali uporabiti običajne strani in se ne obremenjevati z ničemer drugim.
Po drugi strani, če potrebujete dostop do velikega dela pomnilnika zaporedno, vam hugepages povečajo zmogljivost. Vendar pa to morate preveriti sami (in ne na primer abstraktne programske opreme) in videti, kaj bo delovalo hitreje.
Dodelitev v pomnilniku
Če programirate v C, veste, da lahko zahtevate poljubno majhne (ali skoraj poljubno velike) količine pomnilnika iz kupčkov s pomočjo malloc(). Recimo, da potrebujete 30 bajtov pomnilnika:
char* mymemory = malloc(30);Programerju se zdi, da 'zahtevate' 30 bajtov pomnilnika iz operacijskega sistema in vrnete kazalec na nek virtualni pomnilnik. A v resnici malloc () je le funkcija C, ki interno kliče funkcije za zahtevanje ali sproščanje pomnilnika iz operacijskega sistema.
Vendar pa je neučinkovito nenehno zahtevati več in več pomnilnika za vsako dodelitev; veliko bolj verjetno je, da je nek segment pomnilnika že sproščen (free()), in ga lahko znova uporabimo. malloc() izvaja precej zapletene algoritme za ponovno uporabo sproščenega pomnilnika.
Kljub temu se to za vas dogaja neopazno, zakaj bi vas to moralo skrbeti? Zato, ker klic free() ne pomeni, da .
Obstaja pojem fragmentacije pomnilnika. V skrajnih primerih obstajajo segmenti kupčka, kjer se uporablja le nekaj bajtov, medtem ko je vse, kar je med njimi, sproščeno. (free()).
Upoštevajte, da je fragmentacija pomnilnika izjemno zapletena tema, in tudi majhne spremembe v programu lahko nanjo močno vplivajo. V večini primerov programi ne povzročajo znatne fragmentacije pomnilnika, vendar morate vedeti, da lahko, če pride do težav s fragmentacijo v nekem delu kupa, hugepages le poslabšajo situacijo.
Izbirno uporaba hugepages
Po branju članka ste ugotovili, kateri deli vašega programa lahko izkoristijo uporabo hugepages in kateri ne. Ali bi jih sploh morali vključiti?
Na srečo lahko uporabite madvise(), da omogočite hugepaging le za tiste dele pomnilnika, kjer bo to koristno.
Najprej preverite, ali hugepages delujejo v načinu madvise(), s pomočjo na začetku članka.
Nato uporabite madvise(), da jedru pokažete, kje natančno naj uporablja hugepages.
#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)Upoštevajte, da je ta metoda le priporočilo jedru za upravljanje pomnilnika. To ne pomeni, da bo jedro samodejno uporabljalo hugepages za določen pomnilnik.
Oglejte si dokumentacijo , da se naučite več o upravljanju pomnilnika in madvise(), to je tema z izjemno strmo krivuljo učenja. Zato, če se želite resnično dobro spoznati z njo, se pripravite na branje in testiranje več tednov, preden pričakujete kakršen koli pozitiven rezultat.
Kaj prebrati?
Imate vprašanje? Pišite v komentarje!
Izvor: habr.com
