Avantajele și dezavantajele HugePages

Avantajele și dezavantajele HugePages

Traducerea articolului pregătit pentru cursanții „Administrator Linux”.

Anterior, am vorbit despre cum să testați și să activați Hugepages pe Linux.
Acest articol va fi util doar dacă aveți de fapt un loc unde să utilizați Hugepages. Am întâlnit o mulțime de oameni care sunt păcăliți de perspectiva că Hugepages va îmbunătăți în mod magic productivitatea. Cu toate acestea, paginarea uriașă este un subiect complex și poate degrada performanța dacă este utilizat incorect.

Partea 1: Verificarea faptului că paginile uriașe sunt activate pe Linux (original aici)

Problemă:
Trebuie să verificați dacă HugePages este activat pe sistemul dvs.

soluţie:
Este destul de simplu:

cat /sys/kernel/mm/transparent_hugepage/enabled

Veți obține ceva de genul acesta:

always [madvise] never

Veți vedea o listă de opțiuni disponibile (întotdeauna, nebunesc, niciodată), iar opțiunea activă în prezent va fi inclusă în paranteze (în mod implicit nebunesc).

nebunesc înseamnă că transparent hugepages activat numai pentru zonele de memorie care solicită în mod explicit utilizarea paginilor uriașe nebunesc(2).

mereu înseamnă că transparent hugepages întotdeauna activat pentru toate procesele. Acest lucru îmbunătățește de obicei performanța, dar dacă aveți un caz de utilizare în care multe procese consumă o cantitate mică de memorie, atunci încărcarea totală a memoriei poate crește dramatic.

nu înseamnă că transparent hugepages nu vor fi incluse chiar și atunci când sunt solicitate folosind madvise. Pentru a afla mai multe, contactați documentație Kernel-urile Linux.

Cum se schimbă valoarea implicită

Opțiunea 1: Schimbă direct sysfs (după repornire, parametrul va reveni la valoarea implicită):

echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled

Opțiunea 2: Schimbați implicit sistemul prin recompilarea nucleului cu o configurație modificată (această opțiune este recomandată numai dacă utilizați un nucleu personalizat):

  • Pentru a seta întotdeauna implicit, utilizați:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Pentru a seta madvise ca implicit, utilizați:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Partea 2: Avantajele și dezavantajele HugePages

Vom încerca să explicăm în mod selectiv avantajele, dezavantajele și posibilele capcane ale utilizării Hugepages. Deoarece un articol complex din punct de vedere tehnologic și pedant va fi probabil greu de înțeles pentru oamenii care sunt înșelați să creadă că Hugepages este un panaceu, voi sacrifica acuratețea pentru simplitate. Merită să rețineți că multe dintre subiecte sunt cu adevărat complexe și, prin urmare, foarte simplificate.

Vă rugăm să rețineți că vorbim despre sisteme x64 pe 86 de biți care rulează Linux și că presupun pur și simplu că sistemul acceptă pagini uriașe transparente (din moment ce nu este un dezavantaj faptul că paginile uriașe nu sunt suprascrise), așa cum este cazul în aproape orice Linux modern. mediu inconjurator.

Voi atașa mai multe descriere tehnică în link-urile de mai jos.

Memorie virtuala

Dacă ești un programator C++, știi că obiectele din memorie au adrese specifice (valori pointer).

Cu toate acestea, aceste adrese nu reflectă neapărat adrese fizice din memorie (adrese RAM). Ele reprezintă adrese în memoria virtuală. Procesorul are un modul special MMU (unitate de gestionare a memoriei) care ajută nucleul să mapeze memoria virtuală într-o locație fizică.

Această abordare are multe avantaje, dar cele mai importante sunt:

  • Performanță (din diverse motive);
  • Izolarea programului, adică niciun program nu poate citi din memoria altui program.

Ce sunt paginile?

Memoria virtuală este împărțită în pagini. Fiecare pagină individuală indică o anumită memorie fizică, poate indica o zonă din RAM sau poate indica o adresă atribuită unui dispozitiv fizic, cum ar fi o placă video.

Majoritatea paginilor cu care vă ocupați fie indică RAM, fie sunt schimbate, ceea ce înseamnă că sunt stocate pe hard disk sau SSD. Nucleul gestionează aspectul fizic al fiecărei pagini. Dacă este accesată o pagină falsificată, nucleul oprește firul de execuție care încearcă să acceseze memorie, citește pagina de pe hard disk/SSD în RAM și apoi continuă executarea firului de execuție.

Acest proces este transparent în flux, ceea ce înseamnă că nu se citește neapărat direct de pe HDD/SSD. Dimensiunea paginilor normale este de 4096 de octeți. Dimensiunea paginilor uriașe este de 2 megaocteți.

Buffer asociativ de traducere (TLB)

Când un program accesează o pagină de memorie, CPU trebuie să știe de pe ce pagină fizică să citească datele (adică să aibă o hartă de adrese virtuale).

Nucleul are o structură de date (tabel de pagini) care conține toate informațiile despre paginile utilizate. Folosind această structură de date, puteți mapa o adresă virtuală la o adresă fizică.

Cu toate acestea, tabelul de pagini este destul de complex și lent, așa că pur și simplu nu putem analiza întreaga structură de date de fiecare dată când orice proces accesează memoria.

Din fericire, procesorul nostru are un TLB care memorează în cache maparea dintre adresele virtuale și cele fizice. Aceasta înseamnă că, deși trebuie să analizăm tabelul de pagini la prima încercare de acces, toate accesările ulterioare la pagină pot fi gestionate în TLB, permițând operarea rapidă.

Deoarece este implementat ca dispozitiv fizic (ceea ce îl face rapid în primul rând), capacitatea sa este limitată. Deci, dacă doriți să accesați mai multe pagini, TLB nu va putea stoca mapările pentru toate, ceea ce face ca programul dumneavoastră să ruleze mult mai lent.

Hugepages vine în ajutor

Deci, ce putem face pentru a evita depășirea TLB? (Presumăm că programul are nevoie în continuare de aceeași cantitate de memorie).

Aici intervine Hugepages. În loc de 4096 de octeți care necesită o singură intrare TLB, o intrare TLB poate indica acum 2 megaocteți. Să presupunem că TLB are 512 intrări, aici fără Hugepages putem potrivi:

4096 b⋅512=2 MB

Atunci cum ne putem compara cu ei:

2 MB⋅512=1 GB

Acesta este motivul pentru care Hugepages este minunat. Ele pot îmbunătăți productivitatea fără prea mult efort. Dar există avertismente semnificative aici.

Falsificarea paginilor uriașe

Nucleul monitorizează automat cât de des este utilizată fiecare pagină de memorie. Dacă nu există suficientă memorie fizică (RAM), nucleul va muta paginile mai puțin importante (utilizate mai puțin frecvent) pe hard disk pentru a elibera puțin RAM pentru pagini mai importante.
În principiu, același lucru este valabil și pentru Hugepages. Cu toate acestea, nucleul poate schimba numai pagini întregi, nu octeți individuali.

Să presupunem că avem un program ca acesta:

char* mymemory = malloc(2*1024*1024); // Возьмем это за одну Hugepage!
// Заполним mymemory какими-либо данными
// Сделаем много других вещей,
// которые приведут к подмене страницы mymemory
// ...
// Запросим доступ только к первому байту
putchar(mymemory[0]); 

În acest caz, nucleul va trebui să înlocuiască (să citească) până la 2 megaocteți de informații de pe hard disk/SSD doar pentru ca tu să citești un octet. Ca și în cazul paginilor obișnuite, de pe hard disk/SSD trebuie citiți doar 4096 de octeți.

Prin urmare, dacă pagina uriașă este suprascrisă, este mai rapid de citit doar dacă trebuie să accesați întreaga pagină. Aceasta înseamnă că, dacă încercați să accesați aleatoriu diferite părți ale memoriei și citiți doar câțiva kiloocteți, ar trebui să utilizați pagini obișnuite și să nu vă faceți griji pentru nimic altceva.

Pe de altă parte, dacă trebuie să accesați secvențial o mare parte a memoriei, paginile uriașe vă vor îmbunătăți performanța. Cu toate acestea, trebuie să-l testați singur (nu cu software abstract) și să vedeți ce funcționează mai repede.

Alocarea în memorie

Dacă scrieți C, știți că puteți solicita cantități arbitrar mici (sau aproape arbitrar de mari) de memorie din heap folosind malloc(). Să presupunem că aveți nevoie de 30 de octeți de memorie:

char* mymemory = malloc(30);

Pentru un programator, i se poate părea că „cereți” 30 de octeți de memorie de la sistemul de operare și returnați un pointer către o memorie virtuală. Dar de fapt malloc () este doar o funcție C care apelează din interiorul funcției brk și sbrk pentru a solicita sau elibera memorie din sistemul de operare.

Cu toate acestea, solicitarea din ce în ce mai multă memorie pentru fiecare alocare este ineficientă; cel mai probabil, un segment de memorie a fost deja eliberat (free()), și îl putem reutiliza. malloc() implementează algoritmi destul de complexi pentru reutilizarea memoriei eliberate.

În același timp, totul se întâmplă neobservat pentru tine, așa că de ce ar trebui să te îngrijoreze? Dar pentru că provocarea free() nu înseamnă asta memoria este neapărat returnată imediat sistemului de operare.

Există așa ceva ca fragmentarea memoriei. În cazuri extreme, există segmente heap în care sunt utilizați doar câțiva octeți, în timp ce totul dintre acestea a fost eliberat (free()).

Vă rugăm să rețineți că fragmentarea memoriei este un subiect incredibil de complex și chiar și modificările minore ale unui program pot avea un impact semnificativ. În cele mai multe cazuri, programele nu vor provoca o fragmentare semnificativă a memoriei, dar ar trebui să fiți conștienți de faptul că, dacă există o problemă cu fragmentarea într-o anumită zonă a heap-ului, paginile uriașe pot înrăutăți situația.

Utilizarea selectivă a paginilor uriașe

După ce ați citit acest articol, ați stabilit ce părți ale programului dvs. pot beneficia de utilizarea paginilor uriașe și care nu. Deci, ar trebui să fie activate paginile uriașe?

Din fericire, poți folosi madvise()pentru a activa paginarea uriașă numai pentru acele zone de memorie în care ar fi util.

Mai întâi, verificați dacă hugepages rulează în modul madvise() folosind instrucțiuni la începutul articolului.

Apoi, folosește madvise()pentru a spune nucleului exact unde să folosească paginile uriașe.

#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)

Rețineți că această metodă este pur și simplu un sfat pentru nucleu despre cum să gestionați memoria. Acest lucru nu înseamnă că nucleul va folosi automat pagini uriașe pentru o anumită memorie.

Consultați documentația (pagina de manual) sfat nebunpentru a afla mai multe despre gestionarea memoriei și madvise(), acest subiect are o curbă de învățare incredibil de abruptă. Așa că, dacă intenționați să deveniți foarte bun la asta, pregătiți-vă să citiți și să testați câteva săptămâni înainte de a vă aștepta la vreun rezultat pozitiv.

Ce să citești?

A avea o intrebare? Scrieți în comentarii!

Sursa: www.habr.com

Adauga un comentariu