Vantaggi e svantaggi di HugePages

Vantaggi e svantaggi di HugePages

Traduzione dell'articolo preparato per gli studenti del corso "Amministratore Linux".

In precedenza, ho parlato di come testare e abilitare Hugepages su Linux.
Questo articolo sarà utile solo se hai effettivamente un posto dove usare Hugepages. Ho incontrato molte persone che si lasciano ingannare dalla prospettiva che Hugepages possa magicamente migliorare la produttività. Tuttavia, hugepaging è un argomento complesso e può ridurre le prestazioni se utilizzato in modo errato.

Parte 1: verificare che hugepages sia abilitato su Linux (original qui)

Problema:
Devi verificare se HugePages è abilitato sul tuo sistema.

soluzione:
È piuttosto semplice:

cat /sys/kernel/mm/transparent_hugepage/enabled

Otterrai qualcosa del genere:

always [madvise] never

Verrà visualizzato un elenco di opzioni disponibili (sempre, Madvise, mai) e l'opzione attualmente attiva verrà racchiusa tra parentesi (per impostazione predefinita pazzo).

pazzo significa che transparent hugepages abilitato solo per aree di memoria che richiedono esplicitamente l'utilizzo di hugepages pazzo(2).

sempre significa che transparent hugepages sempre abilitato per tutti i processi. Questo di solito migliora le prestazioni, ma se si ha un caso d'uso in cui molti processi consumano una piccola quantità di memoria, il carico di memoria complessivo può aumentare notevolmente.

mai significa che transparent hugepages non saranno inclusi anche se richiesti utilizzando madvise. Per saperne di più, contattare documentazione Kernel Linux.

Come modificare il valore predefinito

opzione 1: Cambia 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: modifica le impostazioni predefinite del sistema ricompilando il kernel con una configurazione modificata (questa opzione è consigliata solo se stai utilizzando un kernel personalizzato):

  • Per impostare sempre per impostazione predefinita, utilizzare:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Per impostare Madvise come predefinito, utilizzare:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Parte 2: Vantaggi e svantaggi di HugePages

Cercheremo di spiegare in modo selettivo i vantaggi, gli svantaggi e le possibili insidie ​​​​dell'utilizzo di Hugepages. Dal momento che un articolo tecnologicamente complesso e pedante sarà probabilmente difficile da comprendere per le persone che si illudono di pensare che Hugepages sia una panacea, sacrificherò la precisione per la semplicità. Vale la pena tenere presente che molti argomenti sono davvero complessi e quindi notevolmente semplificati.

Tieni presente che stiamo parlando di sistemi x64 a 86 bit che eseguono Linux e che sto semplicemente presupponendo che il sistema supporti enormi pagine trasparenti (poiché non è uno svantaggio che le enormi pagine non vengano sovrascritte), come nel caso di quasi tutti i sistemi Linux moderni ambiente.

Allegherò una descrizione più tecnica nei link sottostanti.

Memoria virtuale

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

Tuttavia, questi indirizzi non riflettono necessariamente gli indirizzi fisici in memoria (indirizzi RAM). Rappresentano gli indirizzi nella memoria virtuale. Il processore ha uno speciale modulo MMU (unità di gestione della memoria) che aiuta il kernel a mappare la memoria virtuale in una posizione fisica.

Questo approccio presenta numerosi vantaggi, ma i più importanti sono:

  • Prestazioni (per vari motivi);
  • Isolamento del programma, ovvero nessun programma può leggere dalla memoria di un altro programma.

Cosa sono le pagine?

La memoria virtuale è divisa in pagine. Ogni singola pagina punta a una memoria fisica specifica, può puntare a un'area della RAM oppure può puntare a un indirizzo assegnato a un dispositivo fisico, come una scheda video.

La maggior parte delle pagine che gestisci puntano alla RAM o vengono scambiate, il che significa che sono archiviate sul tuo disco rigido o SSD. Il kernel gestisce il layout fisico di ogni pagina. Se si accede a una pagina contraffatta, il kernel interrompe il thread che sta tentando di accedere alla memoria, legge la pagina dal disco rigido/SSD nella RAM e quindi continua l'esecuzione del thread.

Questo processo è trasparente al flusso, il che significa che non legge necessariamente direttamente dall'HDD/SSD. La dimensione delle pagine normali è 4096 byte. La dimensione di Hugepages è di 2 megabyte.

Buffer associativo alla traduzione (TLB)

Quando un programma accede a una pagina di memoria, la CPU deve sapere da quale pagina fisica leggere i dati (ovvero disporre di una mappa di indirizzi virtuale).

Il kernel ha una struttura dati (tabella delle pagine) che contiene tutte le informazioni sulle pagine utilizzate. Utilizzando questa struttura dati, puoi associare un indirizzo virtuale a un indirizzo fisico.

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

Fortunatamente, il nostro processore ha un TLB che memorizza nella cache la mappatura tra indirizzi virtuali e fisici. Ciò significa che sebbene sia necessario analizzare la tabella delle pagine al primo tentativo di accesso, tutti gli accessi successivi alla pagina possono essere gestiti nel TLB, consentendo operazioni rapide.

Poiché è implementato come dispositivo fisico (e questo lo rende innanzitutto veloce), la sua capacità è limitata. Quindi, se desideri accedere a più pagine, TLB non sarà in grado di memorizzare le mappature per tutte, rallentando notevolmente l'esecuzione del programma.

Hugepages viene in soccorso

Quindi cosa possiamo fare per evitare l’overflow del TLB? (Supponiamo che il programma necessiti ancora della stessa quantità di memoria).

È qui che entra in gioco Hugepages. Invece di 4096 byte che richiedono solo una voce TLB, una voce TLB può ora puntare a ben 2 megabyte. Supponiamo che il TLB abbia 512 voci, qui senza Hugepages possiamo abbinare:

4096 b⋅512=2 MB

Allora come possiamo confrontarci con loro:

2 MB⋅512=1 GB

Questo è il motivo per cui Hugepages è fantastico. Possono migliorare la produttività senza troppi sforzi. Ma qui ci sono avvertenze significative.

Spoofing di Hugepages

Il kernel monitora automaticamente la frequenza con cui viene utilizzata ciascuna pagina di memoria. Se non c'è abbastanza memoria fisica (RAM), il kernel sposterà le pagine meno importanti (usate meno frequentemente) sul disco rigido per liberare RAM per le pagine più importanti.
In linea di principio, lo stesso vale per Hugepages. Tuttavia, il kernel può scambiare solo intere pagine, non singoli byte.

Diciamo che abbiamo un programma come questo:

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

In questo caso, il kernel dovrà sostituire (leggere) fino a 2 megabyte di informazioni dal disco rigido/SSD solo per poter leggere un byte. Per quanto riguarda le pagine normali, devono essere letti solo 4096 byte dal disco rigido/SSD.

Pertanto, se hugepage viene sovrascritto, sarà più veloce da leggere solo se è necessario accedere all'intera pagina. Ciò significa che se stai tentando di accedere in modo casuale a diverse parti della memoria e stai leggendo solo un paio di kilobyte, dovresti utilizzare pagine normali e non preoccuparti di nient'altro.

D'altra parte, se hai bisogno di accedere sequenzialmente a una grande porzione di memoria, hugepages migliorerà le tue prestazioni. Tuttavia, devi testarlo tu stesso (non con software astratto) e vedere cosa funziona più velocemente.

Allocazione in memoria

Se scrivi C, sai che puoi richiedere quantità di memoria arbitrariamente piccole (o quasi arbitrariamente grandi) dall'heap usando malloc(). Diciamo che hai bisogno di 30 byte di memoria:

char* mymemory = malloc(30);

A un programmatore potrebbe sembrare che tu stia "richiedendo" 30 byte di memoria al sistema operativo e restituendo un puntatore a una memoria virtuale. Ma veramente malloc () è solo una funzione C che chiama dall'interno della funzione brk e sbrk per richiedere o liberare memoria dal sistema operativo.

Tuttavia, richiedere sempre più memoria per ciascuna allocazione è inefficiente; è molto probabile che qualche segmento di memoria sia già stato liberato (free())e possiamo riutilizzarlo. malloc() implementa algoritmi piuttosto complessi per riutilizzare la memoria liberata.

Allo stesso tempo, tutto accade inosservato per te, quindi perché dovrebbe preoccuparti? Ma perché la sfida free() non significa questo la memoria viene necessariamente restituita immediatamente al sistema operativo.

Esiste una cosa chiamata frammentazione della memoria. In casi estremi, ci sono segmenti di heap in cui vengono utilizzati solo pochi byte, mentre tutto il resto è stato liberato (free()).

Tieni presente che la frammentazione della memoria è un argomento incredibilmente complesso e anche piccole modifiche a un programma possono avere un impatto significativo. Nella maggior parte dei casi, i programmi non causeranno una frammentazione significativa della memoria, ma dovresti essere consapevole che se c'è un problema di frammentazione in qualche area dell'heap, hugepages può peggiorare la situazione.

Uso selettivo di hugepages

Dopo aver letto questo articolo, hai determinato quali parti del tuo programma possono trarre vantaggio dall'utilizzo di hugepages e quali no. Quindi le enormi pagine dovrebbero essere abilitate?

Per fortuna puoi usare madvise()abilitare l'enorme paginazione solo per quelle aree di memoria in cui sarebbe utile.

Innanzitutto, controlla che hugepages sia in esecuzione in modalità madvise() utilizzando istruzione all'inizio dell'articolo.

Quindi, usa madvise()per dire al kernel esattamente dove usare 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 noti che questo metodo è semplicemente un consiglio al kernel su come gestire la memoria. Ciò non significa che il kernel utilizzerà automaticamente hugepages per una determinata memoria.

Fare riferimento alla documentazione (pagina man)madviseper saperne di più sulla gestione della memoria e madvise(), questo argomento ha una curva di apprendimento incredibilmente ripida. Quindi, se intendi diventare davvero bravo, preparati a leggere e testare per alcune settimane prima di aspettarti risultati positivi.

Cosa leggere?

Hai una domanda? Scrivi nei commenti!

Fonte: habr.com

Aggiungi un commento