Avantages et inconvénients des HugePages

Avantages et inconvénients des HugePages

Traduction de l'article préparé pour les étudiants du cours "Administrateur Linux".

Auparavant, j'ai expliqué comment tester et activer Hugepages sous Linux.
Cet article ne sera utile que si vous disposez réellement d’un endroit pour utiliser Hugepages. J'ai rencontré beaucoup de gens qui sont trompés par la perspective que Hugepages améliorera comme par magie la productivité. Cependant, hugepaging est un sujet complexe et peut dégrader les performances s'il est mal utilisé.

Partie 1 : Vérification que les hugepages sont activés sous Linux (original ici)

Problème:
Vous devez vérifier si HugePages est activé sur votre système.

solution:
C'est assez simple :

cat /sys/kernel/mm/transparent_hugepage/enabled

Vous obtiendrez quelque chose comme ceci :

always [madvise] never

Vous verrez une liste des options disponibles (toujours, madvise, jamais), et l'option actuellement active sera placée entre parenthèses (par défaut fou).

fou signifie que transparent hugepages activé uniquement pour les zones de mémoire qui demandent explicitement des pages énormes en utilisant avis fou (2).

toujours signifie que transparent hugepages toujours activé pour tous les processus. Cela améliore généralement les performances, mais si vous avez un cas d'utilisation dans lequel de nombreux processus consomment une petite quantité de mémoire, la charge globale de la mémoire peut augmenter considérablement.

n'allons jamais signifie que transparent hugepages ne sera pas inclus même sur demande via madvise. Pour en savoir plus, contactez documentation Noyaux Linux.

Comment changer la valeur par défaut

Option 1: Changer directement sysfs (après redémarrage, le paramètre reviendra à sa valeur par défaut) :

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

Option 2: Modifiez la valeur par défaut du système en recompilant le noyau avec une configuration modifiée (cette option n'est recommandée que si vous utilisez un noyau personnalisé) :

  • Pour toujours définir par défaut, utilisez :
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Pour définir madvise par défaut, utilisez :
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Partie 2 : Avantages et inconvénients des HugePages

Nous essaierons d'expliquer de manière sélective les avantages, les inconvénients et les pièges possibles de l'utilisation de Hugepages. Étant donné qu’un article technologiquement complexe et pédant sera probablement difficile à comprendre pour les personnes qui pensent que Hugepages est une panacée, je sacrifierai la précision au profit de la simplicité. Il convient simplement de garder à l’esprit que de nombreux sujets sont très complexes et donc grandement simplifiés.

Veuillez noter que nous parlons de systèmes x64 86 bits exécutant Linux, et que je suppose simplement que le système prend en charge les énormes pages transparentes (puisque ce n'est pas un inconvénient que les énormes pages ne soient pas écrasées), comme c'est le cas dans presque tous les Linux modernes. environnement.

Je joindrai une description plus technique dans les liens ci-dessous.

Mémoire virtuelle

Si vous êtes un programmeur C++, vous savez que les objets en mémoire ont des adresses spécifiques (valeurs de pointeur).

Cependant, ces adresses ne reflètent pas nécessairement les adresses physiques en mémoire (adresses RAM). Ils représentent des adresses dans la mémoire virtuelle. Le processeur dispose d'un module MMU (unité de gestion de la mémoire) spécial qui aide le noyau à mapper la mémoire virtuelle vers un emplacement physique.

Cette approche présente de nombreux avantages, mais les plus importants sont :

  • Performance (pour diverses raisons) ;
  • Isolement du programme, c'est-à-dire qu'aucun programme ne peut lire dans la mémoire d'un autre programme.

Que sont les pages ?

La mémoire virtuelle est divisée en pages. Chaque page individuelle pointe vers une mémoire physique spécifique, elle peut pointer vers une zone de la RAM ou vers une adresse attribuée à un périphérique physique, tel qu'une carte vidéo.

La plupart des pages que vous traitez pointent vers la RAM ou sont échangées, ce qui signifie qu'elles sont stockées sur votre disque dur ou SSD. Le noyau gère la disposition physique de chaque page. Si une page usurpée est consultée, le noyau arrête le thread qui tente d'accéder à la mémoire, lit la page du disque dur/SSD dans la RAM, puis continue d'exécuter le thread.

Ce processus est transparent, ce qui signifie qu'il ne lit pas nécessairement directement à partir du disque dur/SSD. La taille des pages normales est de 4096 octets. La taille des pages énormes est de 2 mégaoctets.

Tampon associatif de traduction (TLB)

Lorsqu'un programme accède à une page de mémoire, le processeur doit savoir à partir de quelle page physique lire les données (c'est-à-dire disposer d'une carte d'adresses virtuelle).

Le noyau possède une structure de données (table de pages) qui contient toutes les informations sur les pages utilisées. À l'aide de cette structure de données, vous pouvez mapper une adresse virtuelle à une adresse physique.

Cependant, la table des pages est assez complexe et lente, nous ne pouvons donc tout simplement pas analyser l'intégralité de la structure des données à chaque fois qu'un processus accède à la mémoire.

Heureusement, notre processeur dispose d'un TLB qui met en cache le mappage entre les adresses virtuelles et physiques. Cela signifie que même si nous devons analyser la table des pages lors de la première tentative d'accès, tous les accès ultérieurs à la page peuvent être gérés dans le TLB, permettant un fonctionnement rapide.

Parce qu’il est implémenté en tant que périphérique physique (ce qui le rend rapide en premier lieu), sa capacité est limitée. Ainsi, si vous souhaitez accéder à plus de pages, le TLB ne pourra pas stocker les mappages pour toutes, ce qui ralentira considérablement l'exécution de votre programme.

Hugepages vient à la rescousse

Alors, que pouvons-nous faire pour éviter le débordement de TLB ? (Nous supposons que le programme a toujours besoin de la même quantité de mémoire).

C'est là qu'intervient Hugepages. Au lieu de 4096 2 octets nécessitant une seule entrée TLB, une entrée TLB peut désormais pointer vers 512 mégaoctets. Supposons que le TLB ait XNUMX entrées, ici sans Hugepages nous pouvons faire correspondre :

4096 b⋅512=2 MB

Alors comment pouvons-nous comparer avec eux :

2 MB⋅512=1 GB

C'est pourquoi Hugepages est génial. Ils peuvent améliorer la productivité sans trop d’effort. Mais il y a ici des mises en garde importantes.

Usurpation de pages énormes

Le noyau surveille automatiquement la fréquence d'utilisation de chaque page mémoire. S'il n'y a pas assez de mémoire physique (RAM), le noyau déplacera les pages moins importantes (moins fréquemment utilisées) vers le disque dur afin de libérer de la RAM pour les pages plus importantes.
En principe, il en va de même pour Hugepages. Cependant, le noyau ne peut échanger que des pages entières, pas des octets individuels.

Disons que nous avons un programme comme celui-ci :

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

Dans ce cas, le noyau devra remplacer (lire) jusqu'à 2 mégaoctets d'informations du disque dur/SSD juste pour que vous puissiez lire un octet. Comme pour les pages normales, seuls 4096 XNUMX octets doivent être lus sur le disque dur/SSD.

Par conséquent, si hugepage est remplacé, la lecture est seulement plus rapide si vous devez accéder à la page entière. Cela signifie que si vous essayez d'accéder de manière aléatoire à différentes parties de la mémoire et que vous ne lisez que quelques kilo-octets, vous devez utiliser des pages normales et ne vous soucier de rien d'autre.

D’un autre côté, si vous devez accéder à une grande partie de la mémoire de manière séquentielle, les pages géantes amélioreront vos performances. Cependant, vous devez le tester vous-même (pas avec un logiciel abstrait) et voir ce qui fonctionne plus rapidement.

Allocation en mémoire

Si vous écrivez C, vous savez que vous pouvez demander des quantités arbitrairement petites (ou presque arbitrairement grandes) de mémoire au tas en utilisant malloc(). Disons que vous avez besoin de 30 octets de mémoire :

char* mymemory = malloc(30);

Pour un programmeur, il peut sembler que vous « demandez » 30 octets de mémoire au système d’exploitation et que vous renvoyez un pointeur vers une mémoire virtuelle. Mais en fait malloc () est juste une fonction C qui appelle depuis la fonction brk et sbrk pour demander ou libérer de la mémoire au système d'exploitation.

Cependant, demander toujours plus de mémoire pour chaque allocation est inefficace ; il est fort probable qu'un segment de mémoire ait déjà été libéré (free()), et nous pouvons le réutiliser. malloc() implémente des algorithmes assez complexes pour réutiliser la mémoire libérée.

En même temps, tout se passe inaperçu pour vous, alors pourquoi cela devrait-il vous inquiéter ? Mais parce que le défi free() ça ne veut pas dire ça la mémoire est nécessairement restituée immédiatement au système d'exploitation.

La fragmentation de la mémoire existe. Dans les cas extrêmes, il existe des segments de tas dans lesquels seuls quelques octets sont utilisés, alors que tout le reste a été libéré. (free()).

Veuillez noter que la fragmentation de la mémoire est un sujet incroyablement complexe et que même des modifications mineures apportées à un programme peuvent avoir un impact significatif. Dans la plupart des cas, les programmes ne provoqueront pas de fragmentation significative de la mémoire, mais vous devez savoir que s'il y a un problème de fragmentation dans une zone du tas, des pages énormes peuvent aggraver la situation.

Utilisation sélective des pages géantes

Après avoir lu cet article, vous avez déterminé quelles parties de votre programme peuvent bénéficier de l'utilisation de hugepages et lesquelles ne le peuvent pas. Alors, les pages géantes devraient-elles être activées ?

Heureusement, vous pouvez utiliser madvise()pour activer hugepaging uniquement pour les zones de mémoire où cela serait utile.

Tout d'abord, vérifiez que hugepages s'exécute en mode madvise() en utilisant instructions au début de l'article.

Ensuite, utilisez madvise()pour indiquer au noyau exactement où utiliser les 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)

Notez que cette méthode est simplement un conseil au noyau sur la façon de gérer la mémoire. Cela ne signifie pas que le noyau utilisera automatiquement des pages énormes pour une mémoire donnée.

Se référer à la documentation (page de manuel)madvisepour en savoir plus sur la gestion de la mémoire et madvise(), ce sujet a une courbe d'apprentissage incroyablement abrupte. Donc, si vous avez l’intention de devenir vraiment bon dans ce domaine, préparez-vous à lire et à tester pendant quelques semaines avant d’espérer des résultats positifs.

Que lire?

Avoir une question? Écrivez dans les commentaires !

Source: habr.com

Ajouter un commentaire