Vous voyez une application ralentir sur des accès mémoire et vous ne savez pas si le problème vient du processeur, du cache ou de la RAM.
La solution la plus rapide consiste à suivre la chaîne complète : l’adresse virtuelle passe par le TLB ou les tables de pages, devient une adresse physique, traverse les caches, puis le contrôleur mémoire la répartit vers un canal, un rang, une banque, une ligne et une colonne DRAM. La correspondance exacte dépend toutefois du processeur et de la plateforme.
Cette lecture s’adresse à vous si vous confondez encore adresse virtuelle et adresse physique, si vous analysez des latences mémoire dans un logiciel, ou si vous étudiez les tables de pages, Rowhammer ou la virtualisation. Elle est également utile pour diagnostiquer un environnement de développement distant qui semble manquer de mémoire alors que la cause réelle est une pression sur le cache, une pagination ou une mauvaise localité des accès.
Le point de départ : une instruction ne voit pas directement la RAM
Prenons une instruction simplifiée :
mov rax, [rdi]
Le registre rdi contient une adresse produite par le programme. Cette valeur est généralement une adresse virtuelle, parfois appelée adresse linéaire sur les architectures x86. Le programme ne sait pas nécessairement dans quelle barrette, quel module ou quelle banque se trouve la donnée. Il demande simplement au processeur de lire les octets associés à cette adresse dans son espace mémoire.
Cette séparation répond à trois contraintes importantes.
- Isolation entre processus : deux programmes peuvent utiliser la même valeur virtuelle, par exemple une adresse proche du début de leur espace utilisateur, sans accéder à la même zone physique.
- Relocalisation : le système d’exploitation peut placer une page physique à un emplacement différent après une allocation, une copie sur écriture ou une opération de compactage.
- Protection : une page peut être marquée comme lisible, modifiable ou exécutable. Une adresse correcte en apparence peut donc provoquer une exception si les droits ne correspondent pas à l’opération demandée.
Sous Linux, chaque processus possède son propre contexte mémoire, associé à une hiérarchie de tables de pages. La documentation du noyau décrit ces tables comme le mécanisme qui relie les adresses virtuelles vues par le processeur aux adresses physiques utilisées sur le bus mémoire. (documentation Linux sur les tables de pages)
Le rôle est réparti entre le logiciel et le matériel :
- le programme manipule des pointeurs et des références ;
- le noyau crée et modifie les mappages ;
- l’unité de gestion mémoire du processeur, ou MMU, effectue les traductions pendant l’exécution ;
- le contrôleur mémoire transforme ensuite l’adresse physique en commande exploitable par la DRAM.
C’est pourquoi demander « quelle puce de RAM contient cette adresse virtuelle ? » revient à sauter plusieurs niveaux de raisonnement.
Comment une adresse virtuelle devient-elle une adresse physique ?
Une adresse virtuelle peut être divisée conceptuellement en deux parties : un numéro de page virtuelle et un déplacement à l’intérieur de cette page. Pour une page de 4 Kio, le déplacement occupe 12 bits, car 4 Kio correspondent à 4 096 octets. Cette valeur est un paramètre d’architecture et de système ; des pages plus grandes peuvent également être utilisées.
Le point essentiel est que le déplacement reste inchangé pendant la traduction. Si le programme demande l’octet situé à l’offset 320 dans une page virtuelle, le processeur cherchera l’offset 320 dans la page physique correspondante. C’est le numéro de page qui est remplacé.
Le parcours logique ressemble à ceci :
adresse virtuelle
│
├── numéro de page virtuelle
└── déplacement dans la page
│
▼
TLB ou parcours des tables de pages
│
▼
numéro de page physique + même déplacement
│
▼
adresse physique
Le TLB : le raccourci indispensable
Le TLB est un cache spécialisé qui mémorise des traductions récentes entre pages virtuelles et pages physiques. Lorsqu’il contient l’entrée recherchée, le processeur évite de relire toute la hiérarchie des tables de pages. Il récupère rapidement le numéro de page physique, conserve le déplacement, puis poursuit l’accès vers la hiérarchie de cache.
Cette distinction est importante pour l’analyse de performance. Un TLB miss n’est pas encore un cache miss de données. Le premier signifie que la traduction n’a pas été trouvée dans le cache de traductions. Le second signifie que les données ne sont pas présentes au niveau de cache consulté.
Les processeurs peuvent également disposer de caches intermédiaires liés au parcours des tables de pages. Leur présence et leur organisation exacte varient selon l’architecture. Le manuel officiel Intel 64 and IA-32 Architectures Software Developer’s Manual documente les mécanismes de pagination, de traduction et de cache pour les processeurs concernés. Pour AMD64, les détails correspondants figurent dans le AMD64 Architecture Programmer’s Manual.
Que se passe-t-il après un TLB miss ?
Après un TLB miss, le processeur ne demande pas immédiatement au noyau de traduire l’adresse. Sur les architectures modernes disposant d’une pagination matérielle, la MMU lance généralement un parcours des tables de pages. Elle utilise un registre architectural contenant l’adresse de la structure supérieure, puis sélectionne successivement les entrées correspondant aux différents morceaux du numéro de page virtuelle.
Pour une hiérarchie à plusieurs niveaux, le parcours peut ressembler à ceci :
registre de base
│
▼
niveau supérieur
│
▼
niveau intermédiaire
│
▼
entrée terminale
│
▼
cadre de page physique
Le nombre de niveaux, la largeur des adresses et les bits de contrôle ne doivent pas être généralisés à tous les processeurs. Linux présente une hiérarchie générique, mais l’architecture peut replier certains niveaux ou utiliser une entrée intermédiaire pour mapper directement une page de grande taille. Les pages géantes réduisent la pression sur le TLB et la quantité de métadonnées nécessaires, mais elles imposent aussi des contraintes d’allocation et peuvent gaspiller de la mémoire si la zone n’est pas réellement utilisée. (documentation Linux sur la hiérarchie des tables)
Les tables de pages contrôlent plus que l’emplacement
Une entrée de table de pages ne contient pas seulement un numéro de page physique. Elle contient également des indicateurs de présence, d’écriture, d’exécution, de niveau de privilège et parfois de comportement de cache.
Cela explique plusieurs situations souvent confondues.
- Une page peut être mappée, mais en lecture seule.
- Une page peut avoir une entrée préparée sans être actuellement présente en mémoire physique.
- Une page peut être partagée entre plusieurs processus avec des droits différents.
- Une page peut être copiée au moment de sa première modification, selon le mécanisme de copie sur écriture.
Lorsque l’entrée est absente ou invalide, la MMU déclenche une faute de page. Cette faute ne signifie pas forcément que le programme est bogué. Elle peut correspondre à une allocation différée, à une page chargée depuis un fichier ou à une page qui doit être restaurée depuis un espace de stockage. En revanche, une adresse interdite ou une tentative d’écriture dans une page protégée peut conduire à une erreur de segmentation.
La documentation Page Tables du noyau Linux décrit notamment la sélection des niveaux, les entrées de pages et les cas où le parcours s’arrête à un niveau supérieur pour mapper une page plus grande. La documentation Examining Process Page Tables aide ensuite à observer certains aspects de la mémoire d’un processus, avec les restrictions de sécurité propres à l’interface.
Dans un environnement de développement distant, cette étape est utile pour distinguer trois problèmes :
- l’application consomme réellement trop de mémoire ;
- elle déclenche de nombreuses fautes de page ;
- elle possède suffisamment de mémoire, mais son modèle d’accès provoque des traductions et des lectures peu efficaces.
Pour relier cette théorie à un cas concret, vous pouvez examiner les caractéristiques d’un environnement Mac distant pour le développement avant de comparer vos propres mesures. La présentation de Kvmzen peut également vous aider à séparer les caractéristiques générales de l’environnement des effets produits par votre programme.
Le cache intervient avant la DRAM
Une fois l’adresse physique obtenue, la donnée n’est pas automatiquement lue dans la DRAM. Le processeur consulte d’abord ses caches de données. Selon l’architecture, plusieurs niveaux peuvent être présents entre le cœur et la mémoire principale.
Pour une lecture, le chemin simplifié est le suivant :
- recherche de la ligne dans le cache le plus proche du cœur ;
- consultation d’un niveau suivant si la ligne est absente ;
- consultation éventuelle du dernier niveau partagé ;
- demande au contrôleur mémoire si tous les niveaux concernés échouent ;
- remplissage des caches avec une ligne contenant la donnée demandée.
Le cache travaille généralement avec des lignes, et non avec un seul octet isolé. Une lecture de quelques octets peut donc entraîner le transfert d’une unité plus large depuis le niveau inférieur. Cette stratégie exploite la localité spatiale : si votre programme lit des éléments voisins d’un tableau, une partie des prochaines données peut déjà être disponible.
Vous devez donc séparer quatre coûts :
- latence de traduction : temps nécessaire pour trouver ou construire la correspondance virtuelle-physique ;
- latence de cache : temps nécessaire pour déterminer si la ligne est présente ;
- latence de file d’attente : attente due à d’autres demandes concurrentes ;
- latence DRAM : temps nécessaire pour activer une ligne, sélectionner une colonne et transférer les données.
Un programme peut être lent sans accéder fréquemment à la DRAM. De même, une application peut avoir un faible taux de défauts de page mais subir de nombreux échecs de cache à cause de structures de données dispersées.
Attention : une adresse physique ne désigne pas directement une ligne et une colonne DRAM. Elle constitue une adresse d’entrée pour le contrôleur mémoire, dont la fonction de répartition dépend de la plateforme.
Comparer les niveaux d’accès pour choisir le bon diagnostic
Le tableau suivant sert de guide de décision lorsque vous observez une latence mémoire. Il ne donne pas de durée universelle : les temps réels dépendent du processeur, de la fréquence, de la charge, du type de mémoire et de la concurrence sur le système.
| Symptôme observé | Niveau probablement concerné | Ce que vous devez vérifier | Décision de diagnostic |
|---|---|---|---|
| Beaucoup de traductions manquantes sur un grand espace mémoire | TLB et tables de pages | Taille des pages, localité des pointeurs, nombre de régions actives | Examiner la pression TLB avant d’accuser la RAM |
| Données absentes des caches mais présentes en mémoire | Cache et modèle d’accès | Parcours séquentiel ou aléatoire, taille des structures, partage entre cœurs | Revoir la disposition des données et la localité |
| Temps variables entre accès pourtant similaires | Contrôleur mémoire et DRAM | Concurrence, banques sollicitées, lignes ouvertes, accès NUMA | Mesurer les conflits plutôt que supposer une panne |
| Fautes de page et activité de stockage | Système de mémoire virtuelle | Allocation différée, pression mémoire, récupération ou échange | Corriger la capacité disponible ou le comportement de l’application |
| Résultats différents dans une machine virtuelle | Traduction imbriquée | Tables invitées, tables de l’hyperviseur, pages géantes | Analyser séparément les deux niveaux d’adresses |
Cette grille évite une erreur fréquente : augmenter la quantité de RAM alors que le problème vient d’une mauvaise localité, d’un cache trop petit pour le jeu de travail ou d’un parcours de pages inefficace.
Comment le contrôleur mémoire choisit-il une position DRAM ?
Après un échec dans les caches, la requête atteint le contrôleur mémoire. C’est lui qui interprète l’adresse physique selon la topologie disponible. Une représentation conceptuelle courante est :
adresse physique
│
├── déplacement dans la ligne de cache
├── canal mémoire
├── rang
├── banque ou groupe de banques
├── ligne
└── colonne
Les termes décrivent des niveaux différents :
- canal : chemin indépendant entre le contrôleur et une partie de la mémoire ;
- rang : ensemble de composants sélectionnés comme une unité sur un module ;
- banque : tableau interne pouvant conserver une ligne active ;
- ligne : rangée de cellules activée dans une banque ;
- colonne : sélection d’une portion de la ligne active lors du transfert.
Une banque DRAM conserve généralement une ligne ouverte dans un tampon de ligne. Si la demande suivante vise la même ligne dans la même banque, le contrôleur peut exploiter cette localité. Si elle vise une autre ligne de cette banque, il doit fermer ou précharger la ligne précédente, activer la nouvelle, puis sélectionner la colonne. Un accès à une autre banque peut permettre davantage de parallélisme, selon les contraintes de commande et de bande passante.
Des travaux de recherche décrivent la DRAM comme une hiérarchie de canaux, rangs, banques, lignes et colonnes, mais ils soulignent également que la fonction de répartition est liée à la conception du contrôleur. L’étude DRAMA : Exploiting DRAM Addressing for Cross-CPU Attacks montre même que des mappages non documentés peuvent être reconstruits expérimentalement sur certaines plateformes.
La conséquence pratique est claire : vous ne pouvez pas publier un schéma binaire universel du type « tels bits sélectionnent toujours la banque et tels autres la ligne ». Le contrôleur peut utiliser de l’interleaving, mélanger certains bits, appliquer une fonction XOR ou modifier le placement selon la configuration mémoire.
L’adresse physique est-elle l’emplacement de la puce RAM ?
Non. L’adresse physique est l’adresse que le sous-système mémoire utilise après la traduction virtuelle. Elle ne constitue pas une étiquette physique visible par le programme indiquant une puce, une barrette ou une cellule précise.
Entre cette adresse et la cellule réellement sollicitée se trouvent notamment :
- la logique du contrôleur mémoire ;
- la configuration des canaux ;
- la sélection des rangs et des banques ;
- les fonctions d’interleaving ;
- les contraintes électriques et temporelles ;
- parfois des mécanismes de remappage internes au module.
Vous pouvez donc connaître une adresse physique sans connaître automatiquement sa banque DRAM. Pour retrouver le mappage d’une plateforme, il faut disposer de documents spécifiques ou employer une méthode expérimentale contrôlée. Les résultats ne doivent pas être reportés d’un processeur à un autre.
Cette nuance compte en sécurité. Les recherches sur Rowhammer et les canaux auxiliaires exploitent parfois les relations entre adresses physiques, banques et lignes, mais les techniques dépendent du matériel étudié. Une observation valide sur une machine ne devient pas une règle générale pour tous les serveurs, ordinateurs portables ou environnements distants.
Une seule instruction de lecture, du pointeur à la DRAM
Reprenons mov rax, [rdi] et suivons-la sans raccourci.
Étape 1 — production de l’adresse virtuelle.
Le programme calcule une adresse à partir d’un pointeur, d’un index de tableau ou d’un champ de structure. Le cœur reçoit cette adresse avec l’opération de lecture.
Étape 2 — contrôle de la traduction.
La MMU consulte le TLB. Si la page virtuelle est présente, elle récupère rapidement le numéro de page physique et conserve le déplacement.
Étape 3 — parcours des tables en cas d’échec.
Si le TLB ne contient pas l’entrée, le processeur parcourt les structures désignées par le registre de base architectural. Les entrées sont contrôlées : présence, droits et niveau de page.
Étape 4 — gestion d’une faute éventuelle.
Si la page n’est pas présente ou si l’accès est interdit, une exception interrompt temporairement l’exécution normale. Le noyau peut charger, créer, partager ou protéger la page, puis relancer l’instruction. Dans un cas invalide, le processus reçoit une erreur.
Étape 5 — formation de l’adresse physique.
Le numéro de page physique est combiné avec le même déplacement. Vous obtenez alors une adresse physique, mais pas encore un emplacement DRAM explicite.
Étape 6 — recherche dans les caches.
Les caches recherchent la ligne correspondant à l’adresse. Une réussite renvoie la donnée sans solliciter la DRAM. Une défaillance déclenche une demande vers le niveau suivant.
Étape 7 — planification par le contrôleur mémoire.
Le contrôleur examine l’adresse physique, la disponibilité des canaux et l’état des banques. Il ordonne les opérations en respectant les contraintes de la mémoire.
Étape 8 — accès à la ligne et à la colonne.
La banque cible active une ligne, puis la commande sélectionne la colonne ou le groupe de données demandé. La ligne de cache est transférée vers le processeur et peut être conservée dans les caches.
Cette séquence explique pourquoi une simple instruction de lecture peut avoir des latences très différentes selon le contexte. La valeur du pointeur n’a pas changé, mais le TLB peut être rempli ou vide, la ligne peut être dans le cache ou non, et la banque DRAM peut déjà avoir la bonne ligne ouverte ou devoir en activer une autre.
Ce qui change en environnement virtualisé
Dans une machine virtuelle, il peut exister deux traductions successives.
- l’adresse virtuelle du programme invité devient une adresse physique invitée ;
- cette adresse est ensuite traduite vers une adresse physique de la machine hôte.
Les extensions de virtualisation peuvent accélérer ce parcours avec des tables imbriquées ou des mécanismes équivalents. Vous devez néanmoins conserver les deux niveaux dans votre modèle mental. Une faute de traduction dans le système invité n’a pas la même cause qu’une faute liée à la mémoire de l’hôte.
Les pages de grande taille peuvent également réduire la pression sur les TLB dans certains scénarios virtualisés. La documentation Linux sur les pages transparentes précise toutefois que le bénéfice dépend de la configuration du noyau invité, de l’hyperviseur et des pages réellement utilisées.
Pour un service distant, la capacité mémoire annoncée ne suffit donc pas à prédire le comportement. Vous devez aussi observer les fautes de page, la récupération mémoire, la taille des pages et les temps d’attente. Avant de comparer plusieurs environnements, vérifiez les conditions contractuelles, les limites de session et les modalités d’accès propres au service étudié.
Méthode de vérification pour vos propres programmes
Lorsque vous soupçonnez un problème de mémoire, avancez dans cet ordre :
- Identifiez le jeu de travail : mesurez la quantité de données réellement parcourue pendant la phase lente.
- Séparez capacité et localité : vérifiez si l’application manque de mémoire ou si elle lit des zones éloignées de manière aléatoire.
- Observez les fautes de page : une hausse indique un problème de présence ou de pression mémoire, pas nécessairement un défaut DRAM.
- Examinez les traductions : les grands espaces d’adresses et les petites pages peuvent accroître la pression sur le TLB.
- Comparez les tailles de pages : les pages géantes peuvent aider certains accès volumineux, mais elles ne conviennent pas automatiquement à toutes les charges.
- Mesurez les caches : distinguez les échecs du cache de données des échecs du TLB.
- Ne déduisez pas la banque DRAM sans preuve : utilisez uniquement la documentation ou une expérimentation liée à la plateforme testée.
- Répétez sur la machine cible : un résultat obtenu sur un poste local ne décrit pas forcément une machine distante ou virtuelle.
Pour les charges audio, vidéo et design, cette méthode est particulièrement utile. Un traitement de piste, un rendu d’aperçu ou la manipulation de textures peut avoir un jeu de travail important, mais la cause d’une irrégularité peut être une disposition de données défavorable plutôt qu’un manque brut de RAM.
Le bon modèle mental à retenir
Pour répondre correctement à la question « comment le CPU trouve une donnée en RAM ? », retenez quatre niveaux.
- Adresse virtuelle : valeur manipulée par le programme dans son espace d’adressage.
- Adresse physique : résultat de la traduction par la MMU et les tables de pages.
- Cache : niveau intermédiaire qui peut servir la donnée avant tout accès à la mémoire principale.
- Position DRAM : résultat d’un mappage réalisé par le contrôleur mémoire, dépendant de la plateforme.
Le CPU ne suit donc pas directement un pointeur jusqu’à une puce. Il traverse une chaîne de traduction, de protection, de mise en cache et de planification. Le TLB accélère les traductions, les tables de pages définissent la relation entre espaces virtuel et physique, les caches évitent de nombreuses lectures DRAM, puis le contrôleur répartit les requêtes selon une organisation matérielle qui peut rester partiellement documentée.
Si votre objectif est de diagnostiquer un environnement de développement, ne vous arrêtez pas à la quantité de mémoire installée. Commencez par vérifier la pression mémoire, les fautes de page, la localité des accès et les échecs de cache. Vous pourrez ensuite rapprocher cette analyse des problèmes d’allocation et de stabilité rencontrés par les charges d’Agent ou les longues compilations.
Pour comparer les caractéristiques générales d’un environnement distant avant de lancer vos mesures, vous pouvez consulter les informations générales du fournisseur étudié. Cette étape ne remplace pas les tests : elle vous aide simplement à distinguer les paramètres du service des effets produits par votre propre charge.
Un poste local ou une machine virtuelle mal dimensionnée peut vous laisser avec des mesures instables, une concurrence invisible entre tâches et des latences difficiles à reproduire. Dans ce cas, louer un environnement Mac auprès de Kvmzen peut offrir un cadre plus simple pour isoler une charge, répéter un test et comparer un comportement mémoire dans le temps. Ce choix reste moins pertinent si vous avez besoin d’une charge permanente très lourde, d’un accès physique à des périphériques ou d’un contrôle matériel complet ; pour une campagne temporaire de développement, de validation ou de rendu, il peut en revanche éviter d’acheter une machine uniquement pour quelques séries de mesures.
