Avantages et inconvénients des HugePages


Une traduction de l'article a été préparée pour les étudiants du cours Administrateur Linux .




Plus tôt, j'ai expliqué comment tester et activer l'utilisation de Hugepages sous Linux.
Cet article ne sera utile que si vous avez vraiment où utiliser Hugepages. J'ai rencontré beaucoup de gens qui sont trompés par la perspective que Hugepages augmentera comme par magie la productivité. Cependant, la pagination énorme est un sujet complexe et, si elle n'est pas utilisée correctement, elle peut réduire les performances.


Partie 1: vérifiez que les pages géantes sont incluses sur Linux (original ici )


Problème:
Vous devez vérifier si les HugePages sont activées 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 ), tandis que l'option active actuelle sera placée entre crochets (par défaut, madvise ).


madvise signifie que les pages géantes transparent hugepages sont incluses que pour les régions de mémoire qui demandent explicitement les pages géantes avec madvise (2) .


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


ne signifie jamais que les transparent hugepages seront pas incluses même lorsqu'elles sont demandées à l'aide de madvise. Voir la documentation du noyau Linux pour plus d' informations .


Comment changer la valeur par défaut


Option 1 : changez sysfs directement (après le redémarrage, le paramètre reviendra à la 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 la configuration modifiée (cette option n'est recommandée que si vous utilisez votre propre noyau):


  • Pour définir toujours la valeur 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 erreurs possibles lors de l'utilisation de Hugepages. Étant donné que l'article technologiquement sophistiqué et pédant est susceptible d'être difficile pour les personnes qui sont trompées en considérant Hugepages comme une panacée, je sacrifierai la précision pour des raisons de simplicité. Il convient de garder à l’esprit que de nombreux sujets sont vraiment complexes et donc grandement simplifiés.


Veuillez noter que nous parlons de systèmes x86 64 bits fonctionnant sous Linux, et que je suppose simplement que le système prend en charge les pages géantes transparentes (car ce n'est pas un inconvénient que les pages géantes ne sont pas remplacées), comme c'est le cas dans presque tous les systèmes modernes. Environnement Linux.


Dans les liens ci-dessous, je joindrai une description plus technique.


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 en RAM). Ce sont des adresses en mémoire virtuelle. Le processeur possède un module spécial MMU (Memory Management Unit) qui aide le noyau à mapper la mémoire virtuelle à un emplacement physique.


Cette approche présente de nombreux avantages, mais les plus fondamentaux d'entre eux:


  • Performance (pour diverses raisons);
  • Isolement des programmes, c'est-à-dire qu'aucun des programmes ne peut lire 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 région de la RAM, ou elle peut pointer vers une adresse affectée à un périphérique physique, comme une carte vidéo.


La plupart des pages que vous traitez pointent soit vers la RAM, soit vers le swap, c'est-à-dire qu'elles sont stockées sur votre disque dur ou SSD. Le noyau contrôle la disposition physique de chaque page. Si une page usurpée est accessible, le noyau arrête le thread qui essaie 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 pour le flux, c'est-à-dire qu'il ne lit pas nécessairement directement à partir du disque dur / SSD. La taille des pages normales est de 4096 octets. Les pages géantes ont une taille de 2 mégaoctets.


Tampon de traduction associatif (TLB)


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


Le noyau a 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, le tableau des pages est plutôt complexe et s'exécute lentement, nous ne pouvons donc tout simplement pas analyser la structure de données entière chaque fois qu'un processus accède à la mémoire.


Heureusement, notre processeur dispose d'un TLB qui met en cache le mappage des adresses virtuelles et physiques. Cela signifie que malgré le fait que nous devons analyser le tableau des pages lors de la première tentative d'accès, tous les appels de pages suivants peuvent être traités dans le TLB, ce qui garantit un fonctionnement rapide.


Puisqu'il est implémenté comme un appareil physique (ce qui le rend principalement rapide), sa capacité est limitée. Par conséquent, si vous souhaitez accéder à plus de pages, TLB ne sera pas en mesure de stocker le mappage pour chacune d'entre elles, de sorte que votre programme fonctionnera beaucoup plus lentement.


D'énormes pages viennent à la rescousse


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


C'est là que les Hugepages apparaissent. Au lieu de 4096 octets, ne nécessitant qu'une seule entrée dans le TLB, une entrée dans le TLB peut désormais pointer vers un énorme 2 mégaoctets. Nous supposerons que le TLB a 512 entrées, ici sans Hugepages, nous pouvons correspondre:


 4096 b⋅512=2 MB 

Alors qu'avec eux on peut comparer:


 2 MB⋅512=1 GB 

C'est pourquoi Hugepages est génial. Ils peuvent augmenter la productivité sans effort significatif. Mais il y a des réserves importantes.


Usurpation de pages énormes


Le noyau suit automatiquement la fréquence d'utilisation de chaque page de mémoire. Si la mémoire physique (RAM) est insuffisante, le noyau déplacera les pages les moins importantes (moins fréquemment utilisées) vers le disque dur pour libérer une partie de la RAM pour les pages plus importantes.
Fondamentalement, il en va de même pour Hugepages. Cependant, le noyau ne peut échanger que des pages entières, pas des octets individuels.


Supposons que nous ayons 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. En ce qui concerne les pages normales, seuls 4096 octets doivent être lus à partir du disque dur / SSD.


Par conséquent, si une énorme page est remplacée, sa lecture n'est plus rapide que 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 de lire seulement quelques kilo-octets, vous devez utiliser des pages régulières et ne vous soucier de rien d'autre.


D'un autre côté, si vous devez accéder à la plupart de la mémoire de manière séquentielle, les pages géantes augmenteront votre productivité. Néanmoins, vous devez vérifier cela vous-même (et non sur l'exemple d'un logiciel abstrait) et voir ce qui fonctionne plus rapidement.


Allocation de mémoire


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


 char* mymemory = malloc(30); 

Il peut sembler au programmeur que vous «demandez» 30 octets de mémoire au système d'exploitation et renvoyez un pointeur vers une certaine mémoire virtuelle. Mais en réalité malloc () n'est qu'une fonction C qui appelle les fonctions brk et sbrk de l'intérieur pour demander ou libérer de la mémoire du système d'exploitation.


Cependant, demander de plus en plus de mémoire pour chaque allocation est inefficace; il est très probable que n'importe quel segment de mémoire a 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 concerner? Mais parce que l'appel à free() ne signifie pas que la mémoire sera retournée immédiatement au système d'exploitation .


Il existe une chose telle que la fragmentation de la mémoire. Dans les cas extrêmes, il existe des segments de tas où seuls quelques octets sont utilisés, tandis que tout le reste a été libéré (free()) .


Notez que la fragmentation de la mémoire est un sujet incroyablement complexe, et même des modifications mineures du programme peuvent l'affecter de manière significative. Dans la plupart des cas, les programmes ne provoquent pas de fragmentation importante de la mémoire, mais vous devez garder à l'esprit que si la fragmentation se produit dans une zone du tas, les pages géantes ne peuvent qu'aggraver la situation.


Application personnalisée d'énormes pages


Après avoir lu l'article, vous avez déterminé quelles parties de votre programme peuvent bénéficier de l'utilisation d'énormes pages et lesquelles ne le peuvent pas. Devrait-on donc inclure les pages énormes?


Heureusement, vous pouvez utiliser madvise() pour activer la pagination énorme uniquement pour les zones de mémoire où cela sera utile.


Pour commencer, vérifiez que les pages géantes fonctionnent en mode madvise (), en utilisant les instructions au début de l'article.


Ensuite, utilisez madvise() pour indiquer au noyau exactement où utiliser énormes pages.


 #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 juste une recommandation au noyau pour la gestion de la mémoire. Cela ne signifie pas que le noyau utilisera automatiquement des pages géantes pour la mémoire donnée.


Consultez la page de manuel madvise pour en savoir plus sur la gestion de la mémoire et madvise() , une courbe d'apprentissage incroyablement abrupte pour ce sujet. Par conséquent, si vous avez l'intention de bien le comprendre, préparez-vous à la lecture et au test pendant plusieurs semaines avant de compter sur au moins un résultat positif.


Que lire?





Vous avez une question? Écrivez dans les commentaires!

Source: https://habr.com/ru/post/fr460403/


All Articles