
La traduction de l'article est préparée pour les étudiants du cours .
Dans un précédent article, j'ai expliqué comment vérifier et activer l'utilisation de Hugepages sous Linux.
Cet article sera utile seulement si vous avez réellement un besoin d'utiliser Hugepages. J'ai rencontré de nombreuses personnes qui se laissent berner par la perspective que les Hugepages amélioreront magiquement les performances. Cependant, les Hugepages sont un sujet complexe et, mal utilisées, elles peuvent en réalité diminuer les performances.
Partie 1 : vérifions si les Hugepages sont activées sous Linux (original) )
Problème :
Il est nécessaire de vérifier si les HugePages sont activées sur votre système.
Solution :
C'est assez simple :
cat /sys/kernel/mm/transparent_hugepage/enabledVous obtiendrez quelque chose comme ceci :
always [madvise] neverVous verrez une liste d'options disponibles (always, madvise, never), avec l'option active actuelle entourée de parenthèses (par défaut, madvise).
madvise signifie que les transparent hugepages sont activées uniquement pour les zones mémoire qui demandent explicitement des Hugepages avec .
always signifie que les transparent hugepages est toujours activée et 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 mémoire totale peut augmenter brusquement.
never signifie que les transparent hugepages ne sera pas activée même si elle est demandée via madvise. Pour en savoir plus, référez-vous à noyau Linux.
Comment modifier la valeur par défaut
Option 1: Modifier directement sysfs (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/enabledOption 2: Modifier la valeur système par défaut en recompilant le noyau avec une configuration modifiée (cette option est recommandée uniquement si vous utilisez votre propre noyau) :
- Pour définir always par défaut, utilisez :
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y # Commenter CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y - Pour définir madvise par défaut, utilisez :
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y # Commenter CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
Partie 2 : Avantages et inconvénients des HugePages
Nous allons essayer d'expliquer de manière sélective les avantages, les inconvénients et les erreurs possibles lors de l'utilisation des Hugepages. Étant donné qu'un article techniquement complexe et minutieux pourrait être difficile à comprendre pour ceux qui se laissent tromper en considérant les Hugepages comme une panacée, je vais sacrifier la précision au profit de la simplicité. Il convient simplement de garder à l'esprit que de nombreux sujets sont réellement complexes et donc fortement simplifiés.
Notez 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 transparent hugepages (car il n'est pas considéré comme un défaut que les hugepages ne soient pas remplacées), comme c'est le cas dans presque tous les environnements Linux modernes.
Dans les liens ci-dessous, je fournirai plus de descriptions techniques.
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). Elles représentent des adresses en mémoire virtuelle. Le processeur dispose d'un module spécial MMU (unité de gestion de la mémoire) qui aide le noyau à faire correspondre la mémoire virtuelle à une localisation physique.
Cette approche présente de nombreux avantages, mais les principaux d'entre eux sont :
- Performance (pour diverses raisons) ;
- Isolation des programmes, c'est-à-dire qu'aucun des programmes ne peut lire la mémoire d'un autre programme.
Qu'est-ce que 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, comme une carte graphique.
La plupart des pages que vous traitez pointent soit vers la RAM, soit sont échangées (swap), c'est-à-dire stockées sur un disque dur ou un SSD. Le noyau gère l'emplacement physique de chaque page. Si un accès à une page échangée est effectué, le noyau interrompt le flux qui essaie d'accéder à la mémoire, lit la page à partir du disque dur/SSD dans la RAM, puis continue l'exécution du flux.
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. La taille des Hugepages est de 2 mégaoctets.
Le tampon de traduction associative (TLB)
Lorsqu'un programme accède à une certaine page de mémoire, le processeur doit savoir quelle page physique lire pour récupérer les données (c'est-à-dire avoir une carte d'adresses virtuelle).
Dans le noyau, il existe une structure de données (table des pages) contenant toutes les informations sur les pages utilisées. Grâce à cette structure de données, il est possible de faire correspondre une adresse virtuelle à une adresse physique.
Cependant, la table des pages est relativement complexe et fonctionne lentement, donc nous ne pouvons pas toujours analyser toute la structure de données chaque fois qu'un processus accède à la mémoire.
Heureusement, notre processeur dispose d'un TLB qui met en cache la correspondance entre les adresses virtuelles et physiques. Cela signifie que bien que nous devions analyser la table des pages lors de la première tentative d'accès, toutes les demandes suivantes pour la même page peuvent être traitées dans le TLB, ce qui garantit un fonctionnement rapide.
Étant donné qu'il est réalisé en tant que périphérique physique (ce qui le rend tout d'abord rapide), sa capacité est limitée. Ainsi, si vous souhaitez accéder à un plus grand nombre de pages, le TLB ne pourra pas stocker la correspondance pour toutes celles-ci, ce qui ralentira considérablement votre programme.
Les Hugepages viennent à la rescousse.
Alors, que pouvons-nous faire pour éviter le débordement du TLB ? (Nous supposons que le programme a toujours besoin de la même quantité de mémoire).
C'est ici que les Hugepages entrent en jeu. Au lieu de 4096 octets, nécessitant une seule entrée dans le TLB, une entrée dans le TLB peut désormais pointer vers d'énormes 2 mégaoctets. Supposons que le TLB ait 512 entrées, ici sans Hugepages, nous pouvons faire correspondre :
4096 o⋅512=2 MoAlors qu'avec eux, nous pouvons faire correspondre :
2 Mo⋅512=1 GoC'est pourquoi les Hugepages sont intéressants. Ils peuvent améliorer les performances sans nécessiter d'efforts significatifs. Mais il y a des réserves importantes.
Substitution des Hugepages
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 moins importantes (utilisées moins fréquemment) vers le disque dur pour libérer une partie de la RAM pour des pages plus importantes.
En principe, il en va de même pour les Hugepages. Cependant, le noyau ne peut échanger que des pages entières, et non des octets individuels.
Supposons que nous avons un programme comme celui-ci :
char* mymemory = malloc(2*1024*1024); // Prenons ceci comme une Hugepage !
// Remplissons mymemory avec des données
// Faisons beaucoup d'autres choses,
// qui entraîneront un remplacement de la page mymemory
// ...
// Demandons l'accès uniquement au premier octet
putchar(mymemory[0]); Dans ce cas, le noyau devra remplacer (lire) 2 mégaoctets d'informations depuis le disque dur/SSD juste pour que vous puissiez lire un octet. En ce qui concerne les pages normales, il suffit de lire 4096 octets depuis le disque dur/SSD.
Ainsi, si une hugepage est remplacée, sa lecture se produit plus rapidement, uniquement si vous devez accéder à toute la page. Cela signifie que si vous essayez d'accéder de manière aléatoire à différentes parties de la mémoire et que vous lisez simplement quelques kilooctets, vous devriez utiliser des pages normales et ne plus vous soucier de rien d'autre.
D'autre part, si vous devez accéder à une grande partie de la mémoire de manière séquentielle, les hugepages amélioreront vos performances. Cependant, vous devez vérifier cela vous-même (et non sur un exemple de logiciel abstrait) et voir ce qui fonctionnera plus rapidement.
Allocation en mémoire
Si vous écrivez en C, vous savez que vous pouvez demander des volumes de mémoire d'un peu (ou presque beaucoup) à la fois depuis le tas avec malloc(). Supposons que vous avez besoin de 30 octets de mémoire :
char* mymemory = malloc(30);Le programmeur peut penser que vous “demandez” 30 octets de mémoire au système d'exploitation et retournez un pointeur vers une certaine mémoire virtuelle. Mais en réalité, malloc () est simplement une fonction C qui appelle en interne les fonctions pour demander ou libérer de la mémoire auprès du système d'exploitation.
Cependant, demander de plus en plus de mémoire pour chaque allocation n'est pas efficace ; 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.
Tout cela se passe de manière transparente pour vous, alors pourquoi devriez-vous vous en soucier ? C'est parce que l'appel free() ne signifie pas que .
Il existe un concept appelé fragmentation de la mémoire. Dans les cas extrêmes, il y a des segments de tas où seule une petite quantité de bytes est utilisée, tandis que tout ce qui se trouve entre eux a été libéré. (free()).
Veuillez noter que la fragmentation de la mémoire est un sujet incroyablement complexe, et même de petits changements dans un programme peuvent avoir un impact significatif sur elle. Dans la plupart des cas, les programmes ne causent pas de fragmentation de mémoire significative, mais vous devez garder à l'esprit que si un problème de fragmentation survient dans une certaine zone de tas, les hugepages peuvent aggraver la situation.
Application sélective des hugepages
Après avoir lu l'article, vous avez déterminé quelles parties de votre programme peuvent tirer parti de l'utilisation des hugepages et lesquelles ne le peuvent pas. Alors, faut-il même activer les hugepages ?
Heureusement, vous pouvez utiliser madvise(), pour activer les hugepages uniquement dans les zones de mémoire où elles seront bénéfiques.
Pour commencer, vérifiez que les hugepages fonctionnent en mode madvise(), à l'aide de au début de l'article.
Ensuite, utilisez madvise(), pour indiquer au noyau où exactement 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)Veuillez noter que cette méthode n'est qu'une recommandation au noyau pour la gestion de la mémoire. Cela ne signifie pas que le noyau utilisera automatiquement les hugepages pour la mémoire spécifiée.
Consultez la documentation , pour en savoir plus sur la gestion de la mémoire et madvise(), ce sujet a une courbe d'apprentissage incroyablement abrupte. Par conséquent, si vous comptez vraiment bien le comprendre, préparez-vous à lire et à tester pendant plusieurs semaines avant d'attendre un quelconque résultat positif.
Que lire ?
Vous avez une question ? Écrivez dans les commentaires !
Source : habr.com
