
Articolul este tradus pentru studenții cursului .
Anterior, am povestit despre cum să verificați și să activați utilizarea Hugepages în Linux.
Acest articol va fi util doar dacă aveți într-adevăr un loc unde să utilizați Hugepages. Am întâlnit multe persoane care sunt induse în eroare de perspectiva că Hugepages vor crește magic performanța. Cu toate acestea, hugepaging este un subiect complicat, iar utilizarea greșită poate duce la scăderea performanței.
Partea 1: verificăm dacă hugepages sunt activate în Linux (original) )
Problema:
Trebuie să verificați dacă HugePages sunt activate în sistemul dumneavoastră.
Soluția:
Este destul de simplu:
cat /sys/kernel/mm/transparent_hugepage/enabledVeți obține ceva de genul acesta:
always [madvise] neverVeți vedea o listă de opțiuni disponibile (always, madvise, never), opțiunea activă în prezent va fi între paranteze (implicit madvise).
madvise înseamnă că transparent hugepages sunt activate doar pentru zonele de memorie care solicită explicit hugepages prin .
always înseamnă că transparent hugepages sunt activate întotdeauna și pentru toate procesele. De obicei, acest lucru crește performanța, dar dacă aveți un caz de utilizare în care multe procese consumă o cantitate mică de memorie, atunci încărcătura totală pe memorie poate crește brusc.
never înseamnă că transparent hugepages nu se vor activa nici măcar la cererea utilizând madvise. Pentru a afla mai multe, consultați nucleul Linux.
Cum să schimbați valoarea implicită
Opțiunea 1: Schimbați direct sysfs (după repornire, parametrul se va întoarce 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/enabledOpțiunea 2: Schimbați valoarea implicită a sistemului recompilând nucleul cu o configurație modificată (această opțiune este recomandată doar dacă utilizați un nucleu personalizat):
- Pentru a seta always ca implicit, utilizați:
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Comentați CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y - Pentru a seta madvise ca implicit, utilizați:
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Comentați CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
Partea 2: Avantajele și dezavantajele HugePages
Vom încerca să explicăm selectiv avantajele, dezavantajele și posibilele capcane ale utilizării Hugepages. Deoarece acest articol tehnologic complicat și meticulos va fi probabil greu de înțeles pentru persoanele care sunt induse în eroare considerând Hugepages o panacee, voi sacrifica precizia în favoarea simplității. Este important de menționat că multe subiecte sunt cu adevărat complicate și, prin urmare, sunt foarte simplificate.
Vă rugăm să rețineți că vorbim despre sisteme x86 de 64 de biți care funcționează pe Linux, și că presupun că sistemul suportă transparent hugepages (deoarece nu este o deficiență faptul că hugepages nu sunt înlocuite), așa cum se întâmplă în aproape orice mediu Linux modern.
În linkurile de mai jos voi atașa mai multe descrieri tehnice.
Memoria virtuală
Dacă sunteți programator C++, știți că obiectele din memorie au adrese specifice (valori ale pointer-ului).
Cu toate acestea, aceste adrese nu reflectă neapărat adresele fizice din memorie (adresele din RAM). Ele reprezintă adrese în memoria virtuală. Procesorul are un modul special MMU (unitate de gestionare a memoriei), care ajută nucleul să mappeze memoria virtuală pe locația fizică.
Această abordare are numeroase avantaje, dar cele mai de bază sunt:
- Performanța (din diverse motive);
- Isolarea programelor, 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 memoria RAM sau poate indica o adresă alocată unui dispozitiv fizic, precum o placă grafică.
Majoritatea paginilor cu care lucrați indică fie pe RAM, fie sunt în swap, adică sunt stocate pe un hard disk sau SSD. Nucleul gestionează localizarea fizică a fiecărei pagini. Dacă se accesează o pagină în swap, nucleul oprește firul care încearcă să acceseze memoria, citește pagina de pe hard disk/SSD în memoria RAM și apoi continuă execuția firului.
Acest proces este transparent pentru fir, adică nu citește neapărat direct de pe hard disk/SSD. Dimensiunea paginilor normale este de 4096 octeți. Dimensiunea Hugepages este de 2 megabytes.
Bufferul de traducere asociativă (TLB)
Când un program accesează o anumită pagină de memorie, centrala procesare trebuie să știe de pe care pagină fizică să citească datele (adică să aibă o hartă a adreselor virtuale).
În nucleu există o structură de date (tabelul paginilor) care conține toate informațiile despre paginile utilizate. Cu ajutorul acestei structuri de date, se poate mapa o adresă virtuală la o adresă fizică.
Însă, tabelul paginilor este destul de complex și funcționează lent, așa că nu putem analiza întreaga structură de date de fiecare dată când un proces accesează memoria.
Din fericire, procesorul nostru are un TLB care face cache pentru maparea adreselor virtuale și fizice. Asta înseamnă că, deși trebuie să analizăm tabelul paginilor la prima încercare de acces, toate accesurile ulterioare la pagină pot fi gestionate în TLB, ceea ce asigură o funcționare rapidă.
Deoarece este implementat ca un dispozitiv fizic (ceea ce îl face rapid în primul rând), capacitatea sa este limitată. Prin urmare, dacă vrei să accesezi un număr mai mare de pagini, TLB-ul nu va putea stoca maparea pentru toate acestea, ceea ce va face ca programul tău să funcționeze mult mai lent.
Hugepages vin în ajutor
Deci, ce putem face pentru a evita supraaglomerarea TLB? (Presupunem că programul are încă nevoie de aceeași cantitate de memorie).
Aici intervin Hugepages. În loc de 4096 de biți, care necesită un singur entry în TLB, un entry în TLB poate acum să indice o colosală 2 megabytes. Să presupunem că TLB-ul are 512 intrări, fără Hugepages putem mapa:
4096 b⋅512=2 MBÎn timp ce cu ele putem mapa:
2 MB⋅512=1 GBDe aceea Hugepages sunt grozave. Ele pot îmbunătăți performanța fără un efort semnificativ. Dar există câteva limitări importante.
Substituirea Hugepages
Nucleul urmărește automat frecvența de utilizare a fiecărei pagini de memorie. Dacă memoria fizică (RAM) este insuficientă, nucleul va muta paginile mai puțin importante (mai rar utilizate) pe hard disk pentru a elibera o parte din RAM pentru paginile mai importante.
În principiu, același lucru este valabil și pentru Hugepages. Cu toate acestea, nucleul poate schimba doar paginile întregi, nu și biții individuali.
Să presupunem că avem un program de acest tip:
char* mymemory = malloc(2*1024*1024); // Să considerăm aceasta o Hugepage!\n// Vom umple mymemory cu date\n// Vom face multe alte lucruri,\n// care vor duce la substituirea paginii mymemory\n// ...\n// Vom solicita acces doar la prima unitate\nprintf(mymemory[0]); În acest caz, nucleului îi va trebui să înlocuiască (citească) întreaga informație de 2 megabytes de pe hard disk/SSD doar pentru a citi un singur byte. Cât despre paginile obișnuite, de pe hard disk/SSD trebuie să fie citite doar 4096 byte.
Prin urmare, dacă hugepage este înlocuit, citirea acestuia se realizează mai repede, doar dacă trebuie să aveți acces la întreaga pagină. Asta înseamnă că, dacă încercați să accesați aleatoriu diferite părți ale memoriei și pur și simplu citiți câțiva kilobytes, ar trebui să folosiți pagini obișnuite și să nu vă mai faceți griji.
Pe de altă parte, dacă aveți nevoie să accesați o mare parte din memorie secvențial, hugepages vă vor îmbunătăți performanța. Cu toate acestea, trebuie să verificați acest aspect singuri (nu pe baza unui software abstract) și să vedeți ce va funcționa mai repede.
Alocarea în memorie
Dacă scrieți în C, știți că puteți solicita orice cantitate mică (sau aproape orice cantitate mare) de memorie din heap folosind malloc(). Să presupunem că aveți nevoie de 30 byte de memorie:
char* mymemory = malloc(30);Programatorului i se poate părea că „solicitați” 30 byte de memorie de la sistemul de operare și returnați un pointer către unele memorie virtuală. Dar, de fapt, malloc () este pur și simplu o funcție C care apelează din interior funcțiile pentru a solicita sau a elibera memorie de la sistemul de operare.
Cu toate acestea, a solicita din ce în ce mai multă memorie pentru fiecare alocare nu este eficient; cel mai probabil, un segment de memorie a fost deja eliberat (free()), și îl putem folosi din nou. malloc() implementează algoritmi destul de complicati pentru reutilizarea memoriei eliberate.
În acest timp, totul se întâmplă invizibil pentru voi, așa că de ce ar trebui să vă preocupe? Deoarece apelul free() nu înseamnă că .
Există un concept numit fragmentare a memoriei. În cazuri extreme, există segmente de heap unde sunt folosiți doar câțiva byte, în timp ce tot ceea ce se află între ele a fost eliberat. (free()).
Este important de menționat că fragmentarea memoriei este un subiect extrem de complex, iar chiar și mici modificări în program pot avea un impact semnificativ asupra acesteia. În cele mai multe cazuri, programele nu cauzează o fragmentare semnificativă a memoriei, dar trebuie să aveți în vedere că, dacă există o problemă de fragmentare într-o anumită zonă a heap-ului, hugepages ar putea agrava situația.
Utilizarea selectivă a hugepages
După ce ați citit articolul, ați identificat ce părți ale programului dumneavoastră pot beneficia de utilizarea hugepages și care nu. Așadar, ar trebui să activați hugepages?
Din fericire, puteți folosi madvise(), pentru a activa hugepaging doar pentru acele zone de memorie unde acesta ar fi util.
În primul rând, verificați că hugepages funcționează în modul madvise(), folosind la începutul articolului.
Apoi, folosiți madvise(), pentru a indica nucleului unde să folosească 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)Este important de reținut că această metodă reprezintă doar o recomandare pentru nucleu în privința gestionării memoriei. Asta nu înseamnă că nucleul va folosi automat hugepages pentru memoria specificată.
Consultați documentația , pentru a învăța mai multe despre gestionarea memoriei și madvise(), acest subiect are o curba de învățare incredibil de abruptă. Așadar, dacă doriți să înțelegeți cu adevărat, pregătiți-vă să citiți și să testați timp de câteva săptămâni înainte de a vă aștepta la vreun rezultat pozitiv.
Ce să citiți?
Aveți întrebări? Lăsați un comentariu!
Sursa: habr.com
