Vantaggi e svantaggi di HugePages

Vantaggi e svantaggi di HugePages

La traduzione dell'articolo è stata preparata per gli studenti del corso «Amministratore Linux».

In precedenza, ho spiegato come controllare e abilitare l'uso delle Hugepages in Linux.
Questo articolo sarà utile solo se hai effettivamente un luogo dove utilizzare le Hugepages. Ho incontrato molte persone ingannate dalla prospettiva che le Hugepages possano migliorare magicamente le prestazioni. Tuttavia, l'hugapaging è un argomento complesso e, se usato in modo errato, può ridurre le prestazioni.

Parte 1: verifichiamo che le Hugepages siano attive in Linux (originale qui)

Problema:
Devi controllare se le HugePages sono attive nel tuo sistema.

Soluzione:
La procedura è piuttosto semplice:

cat /sys/kernel/mm/transparent_hugepage/enabled

Otterrai qualcosa di simile:

always [madvise] never

Vedrai un elenco delle opzioni disponibili (always, madvise, never), con l'opzione attiva corrente delimitata da parentesi (per impostazione predefinita madvise).

madvise significa che significa che le transparent hugepages sono attive solo per le aree di memoria che richiedono esplicitamente hugepages tramite.

always significa che significa che madvise(2)

mai significa che significa che non verranno abilitati nemmeno quando richiesto tramite madvise. Per saperne di più, consultare il documentazione nucleo Linux.

Come modificare il valore predefinito

Opzione 1: Modificare direttamente sysfs (dopo il riavvio, il parametro tornerà al valore predefinito):

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

Opzione 2: Cambiare il valore di sistema predefinito ricompilando il kernel con una configurazione modificata (questa opzione è consigliata solo se stai usando un kernel personalizzato):

  • Per impostare always come predefinito, utilizzare:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Commenta CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Per impostare madvise come predefinito, utilizzare:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Commenta CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Parte 2: Vantaggi e svantaggi di HugePages

Cercheremo di spiegare selettivamente i vantaggi, gli svantaggi e i possibili errori nell'uso delle Hugepages. Poiché si tratta di un argomento tecnicamente complesso e pedante, potrebbe risultare difficile da comprendere per chi si illude che le Hugepages siano una panacea; sacrifierò la precisione a favore della semplicità. È importante tenere presente che molti temi sono davvero complessi e quindi sono stati semplificati notevolmente.

Si noti che parliamo di sistemi x86 a 64 bit che operano su Linux, e che suppongo semplicemente che il sistema supporti le transparent hugepages (poiché non è un difetto che le hugepages non siano sostituite), come avviene praticamente in qualsiasi moderna ambientazione Linux.

Nei link sottostanti, includerò ulteriori descrizioni tecniche.

Memoria virtuale

Se sei uno sviluppatore C++, sai che gli oggetti in memoria hanno indirizzi specifici (valori dei puntatori).

Tuttavia, questi indirizzi non devono necessariamente riflettere gli indirizzi fisici nella memoria (indirizzi nella RAM). Rappresentano indirizzi nella memoria virtuale. Il processore ha un'unità speciale chiamata MMU (unità di gestione della memoria), che aiuta il core a mappare la memoria virtuale con la posizione fisica.

Questo approccio ha molti vantaggi, ma i principali sono:

  • Prestazioni (per vari motivi);
  • Isolamento dei programmi, cioè nessun programma può leggere dalla memoria di un altro programma.

Cosa sono le pagine?

La memoria virtuale è suddivisa in pagine. Ogni singola pagina punta a una specifica memoria fisica, può puntare a un'area nella memoria operativa oppure a un indirizzo assegnato a un dispositivo fisico, come ad esempio una scheda video.

La maggior parte delle pagine con cui hai a che fare si riferisce o alla RAM oppure sono swap, cioè memorizzate su disco rigido o SSD. Il kernel gestisce la posizione fisica di ogni pagina. Se viene accessibile una pagina swap, il kernel interrompe il thread che sta cercando di accedere alla memoria, legge la pagina dal disco rigido/SSD nella RAM e poi riprende l'esecuzione del thread.

Questo processo è trasparente per il thread, cioè non legge necessariamente direttamente dal disco rigido/SSD. La dimensione delle pagine normali è di 4096 byte. La dimensione delle Hugepages è di 2 megabyte.

Buffer di traduzione associativa (TLB)

Quando un programma accede a una certa pagina di memoria, la CPU deve sapere da quale pagina fisica leggere i dati (cioè deve avere una mappa degli indirizzi virtuali).

Nel kernel c'è una struttura dati (tabella delle pagine) che contiene tutte le informazioni sulle pagine utilizzate. Con questa struttura dati è possibile mappare un indirizzo virtuale a un indirizzo fisico.

Tuttavia, la tabella delle pagine è piuttosto complessa e funziona lentamente, quindi non possiamo analizzare ogni volta l'intera struttura dei dati quando un processo accede alla memoria.

Fortunatamente, il nostro processore ha un TLB che memorizza nella cache l'associazione tra indirizzi virtuali e fisici. Questo significa che, sebbene dobbiamo analizzare la tabella delle pagine alla prima richiesta di accesso, tutte le successive richieste alla pagina possono essere gestite nel TLB, assicurando così un funzionamento rapido.

Poiché è implementato come dispositivo fisico (il che lo rende principalmente veloce), la sua capacità è limitata. Pertanto, se si desidera accedere a un numero maggiore di pagine, il TLB non sarà in grado di memorizzare l'associazione per tutte, il che farà rallentare notevolmente il funzionamento del programma.

Hugepages vengono in nostro aiuto

Quindi, cosa possiamo fare per evitare di sovraccaricare il TLB? (Supponiamo che il programma abbia ancora bisogno della stessa quantità di memoria).

Ecco dove entrano in gioco le Hugepages. Invece di 4096 byte, che richiedono solo una voce nel TLB, una voce nel TLB ora può puntare a enormi 2 megabyte. Supponiamo che il TLB abbia 512 voci; senza Hugepages possiamo abbinare:

4096 b⋅512=2 MB

Mentre con esse possiamo abbinare:

2 MB⋅512=1 GB

Ecco perché le Hugepages sono fantastiche. Possono migliorare le prestazioni senza un notevole sforzo. Ma ci sono delle sostanziali riserve.

Sostituzione delle Hugepages

Il kernel tiene automaticamente traccia della frequenza di utilizzo di ogni pagina di memoria. Se la memoria fisica (RAM) è insufficiente, il kernel sposterà le pagine meno importanti (meno utilizzate) su disco rigido per liberare parte della RAM per pagine più importanti.
In linea di principio, lo stesso vale per le Hugepages. Tuttavia, il kernel può scambiare solo intere pagine, non singoli byte.

Supponiamo di avere un programma come questo:

char* mymemory = malloc(2*1024*1024); // Consideriamo questo come una Hugepage!
// Riempiamo mymemory con dei dati
// Facciamo molte altre cose,
// che porteranno alla sostituzione della pagina mymemory
// ...
// Richiediamo accesso solo al primo byte
putchar(mymemory[0]); 

In questo caso, il kernel deve leggere 2 megabyte di informazioni dall'hard disk/SSD solo per permetterti di leggere un byte. Per quanto riguarda le pagine normali, è necessario leggere solo 4096 byte dall'hard disk/SSD.

Pertanto, se hugepage viene sostituita, la sua lettura avviene più rapidamente solo se hai bisogno di accedere all'intera pagina. Questo significa che se stai cercando di accedere casualmente a diverse parti della memoria e stai semplicemente leggendo un paio di kilobyte, dovresti usare pagine normali e non preoccuparti di nulla di più.

D'altra parte, se hai bisogno di accedere a una grande parte della memoria in modo sequenziale, le hugepages aumenteranno le tue prestazioni. Tuttavia, è necessario verificare personalmente (e non con un software astratto) per vedere cosa funziona più velocemente.

Allocazione in memoria

Se scrivi in C, sai che puoi richiedere quantità di memoria praticamente minime (o quasi massime) dalla heap usando malloc(). Supponiamo che tu abbia bisogno di 30 byte di memoria:

char* mymemory = malloc(30);

Per un programmatore, può sembrare che stia "richiedendo" 30 byte di memoria dal sistema operativo e restituendo un puntatore a una certa memoria virtuale. Ma in realtà malloc() è semplicemente una funzione C che chiama internamente le funzioni brk e sbrk per richiedere o liberare memoria dal sistema operativo.

Tuttavia, richiedere sempre più memoria per ogni allocazione non è efficiente; è molto probabile che un qualche segmento di memoria sia già stato liberato (free()), e noi possiamo riutilizzarlo. malloc() implementa algoritmi piuttosto complessi per il riutilizzo della memoria liberata.

Allo stesso modo, per te tutto avviene senza farti accorgere, quindi perché dovrebbe preoccuparti? Perché la chiamata free() non significa che la memoria venga necessariamente restituita immediatamente al sistema operativo..

Esiste un concetto chiamato frammentazione della memoria. In casi estremi, ci sono segmenti di heap che utilizzano solo pochi byte, mentre tutto ciò che si trova tra di essi è stato liberato. (free()).

Si prega di notare che la frammentazione della memoria è un argomento incredibilmente complesso, e anche piccole modifiche nel programma possono influenzarla notevolmente. Nella maggior parte dei casi, i programmi non causano una significativa frammentazione della memoria, ma dovete tenere presente che se c'è un problema di frammentazione in una certa area della heap, l'uso di hugepages potrebbe solo aggravare la situazione.

Applicazione selettiva di hugepages

Dopo aver letto l'articolo, avete identificato quali parti del vostro programma possono beneficiare dell'uso di hugepages e quali no. Dovete quindi includere hugepages?

Fortunatamente, potete utilizzare madvise(), per abilitare hugepaging solo per quelle aree di memoria in cui sarà utile.

Per iniziare, verificate che hugepages funzionino in modalità madvise(), utilizzando documentazione all'inizio dell'articolo.

Poi, usate madvise(), per indicare al kernel dove utilizzare 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)

Si prega di notare che questo metodo è solo un consiglio al kernel sulla gestione della memoria. Questo non significa che il kernel utilizzerà automaticamente hugepages per la memoria specificata.

Fate riferimento alla documentazione (manpage) madvise, per saperne di più sulla gestione della memoria e madvise(), questa tematica ha una curva di apprendimento incredibilmente interessante. Pertanto, se intendi davvero padroneggiarla, preparati a leggere e testare per diverse settimane prima di aspettarti qualche risultato positivo.

Cosa leggere?

Hai domande? Scrivi nei commenti!

Fonte: habr.com

Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster