Maison > Article > Tutoriel système > Optimisation des performances au niveau du pipeline x86
Présentation | Comment choisir le bon chemin face à une branche ? Si la sélection d'instructions est erronée, l'ensemble du pipeline doit attendre que les instructions restantes soient exécutées, les effacer, puis recommencer à partir de la position correcte. Plus le niveau du pipeline est profond, plus les dégâts causés sont importants. |
Optimisation des performances, la clé est de bien servir le CPU. En tant que programmeur qui recherche le summum des performances, comprendre les mécanismes internes du processeur est un sujet incontournable. Il s'agit d'un processus continu qui nécessite une accumulation au fil du temps, mais il n'est pas nécessaire d'approfondir les circuits numériques. Tout comme un expert en conception de processeurs ne doit pas nécessairement maîtriser la conception de logiciels, vous n'avez pas besoin d'être un expert en processeurs. pour écrire des logiciels performants de haut niveau.
En tant que cadeau précieux d'un petit groupe d'élites humaines au grand public, les processeurs pouvant être achetés à volonté sur le marché représentent en réalité le niveau technologique le plus avancé de l'humanité, tout comme les armes nucléaires qui ne peuvent pas être achetées. Même un expert en CPU x86 ne peut parler en détail que de ce dans quoi il se spécialise. Pour nous, même s’il est impossible de tout comprendre, trois parties sont très critiques : le pipeline, le cache et le jeu d’instructions. Parmi ces trois parties, la « chaîne de montage » peut être utilisée comme indice de fonctionnement. Par conséquent, en suivant l’exemple de l’article précédent, examinons d’abord le pipeline.
Notions de base
La tâche principale de PU est d'effectuer des opérations sur les données conformément aux instructions. Cette phrase explique essentiellement ce qu'est une chaîne de montage. Je sais que personne qui peut cliquer sur cet article ne sait rien du concept de « chaîne de montage ». Je ne veux pas présenter au début un gros texte de type manuel et énumérer les définitions de divers concepts. Abandonnez de tout cœur les fondamentaux et poursuivez ce qui est inférieur. Le développement de la technologie n'est qu'une forme de mouvement de contradictions dans les choses. Cette fois, nous essaierons de présenter les différents composants du pipeline du point de vue de l'évolution historique du CPU.
Depuis qu'Intel a produit le premier processeur 8086 il y a 40 ans jusqu'à aujourd'hui, les changements apportés aux processeurs vous ont donné l'impression que les processeurs précédents ne peuvent être appelés que des « ordinateurs monopuce ». Mais même s'il s'agit d'un micro-ordinateur monopuce qui ne coûte que quelques centimes sur Taobao, il présente tout de même quelques similitudes avec le processeur i7 actuel. Le processeur 8086 dispose de 14 registres qui sont encore utilisés aujourd'hui : 4 registres à usage général (General Purpose Register), 4 registres de segments (Segment Register), 4 registres d'index (Index Register) et 1 registre d'indicateur (EFLAGS Register). Il est utilisé pour marque l'état du processeur, et le dernier, le registre de pointeur d'instruction, est utilisé pour enregistrer l'adresse de la prochaine instruction qui doit être exécutée. Ce registre de pointeurs d'instructions est directement lié au processus d'exploitation du pipeline. Son existence continue montre également la cohérence temporelle des principes de base du pipeline.
Depuis 40 ans jusqu'à nos jours, toutes les instructions exécutées par le CPU suivent le processus suivant : le CPU obtient d'abord (Fetch) l'adresse de l'instruction à exécuter dans le segment de code en fonction du pointeur d'instruction, puis décode (Decode ) l'instruction à l'adresse. Après le décodage, il entrera dans la phase d'exécution proprement dite (Execute), suivie de la phase "Write Back", où le résultat final du traitement est réécrit dans la mémoire ou le registre, et le registre du pointeur d'instruction est mis à jour pour pointer vers le instruction suivante. Il s’agit essentiellement d’une solution de conception totalement cohérente avec la logique humaine.
Au départ, et le plus naturellement, le CPU traitera toutes les instructions les unes après les autres. Chaque instruction est exécutée selon le processus ci-dessus, puis l'instruction suivante est exécutée. La principale contradiction à cette époque était la contradiction entre les exigences croissantes de performances des logiciels et la vitesse de traitement rétrograde du processeur. Sous la direction correcte de la loi de Moore, les travaux de construction du CPU ont obtenu des résultats historiques et la principale contradiction a changé : la vitesse d'exécution du CPU a lentement dépassé la vitesse de lecture et d'écriture de la mémoire. Ainsi, récupérer à chaque fois des instructions de la mémoire devenait de plus en plus insupportable, c'est pourquoi en 1982, un cache d'instructions a été introduit dans le processeur.
À mesure que les processeurs deviennent de plus en plus rapides, la mise en cache des données est également introduite dans le processeur comme compromis entre les deux parties en conflit. Mais ce ne sont pas des solutions permanentes. Le principal aspect de la contradiction est que le CPU ne fonctionne pas à saturation. Ainsi, en 1989, le processeur i486 a introduit de manière constructive un pipeline en cinq étapes. L’idée est de digérer la capacité excédentaire du CPU en stimulant la demande intérieure : au lieu de traiter une seule instruction à la fois, il peut en traiter cinq à la fois.
Du niveau du pipeline x86, parlons de la façon d'optimiser les performancesJe ne sais pas ce que vous pensez, mais j’ai toujours du mal à comprendre cette image. Pour une compréhension simple : imaginez chaque instruction comme un produit à traiter, circulant dans une chaîne d'assemblage avec 5 étapes de traitement. Cela permet à chaque processus du processeur de toujours maintenir une charge de travail saturée, ce qui améliore fondamentalement le débit des instructions et les performances du programme.
Problèmes introduits par la chaîne de montage
Si vous résumez simplement chaque ligne de code dans une instruction XOR, selon le diagramme de pipeline i486 ci-dessus, la première instruction entre dans l'étape Fetch du pipeline, puis entre dans l'étape D1, moment auquel la deuxième instruction entre dans Fetch. Au cycle machine suivant, la première instruction passe dans D2, la seconde dans D1 et la troisième instruction est récupérée. Jusqu'à présent, tout est normal, mais lors du cycle machine suivant, lorsque la première instruction entre dans l'étape d'exécution, la deuxième instruction ne peut pas continuer à entrer dans l'étape suivante, car le résultat final de la variable a dont elle a besoin doit être dans la première. Elle ne peut que être obtenu après l’exécution de l’instruction. Par conséquent, la deuxième instruction sera bloquée sur le pipeline et ne continuera pas tant que la première instruction n’est pas terminée. Lors de l'exécution de la deuxième instruction, la troisième instruction aura une rencontre similaire. Lorsqu'un blocage du pipeline se produit, l'exécution des instructions par le pipeline sera séparée de l'exécution individuelle, appelée « bulle » du pipeline.
Cycle d'horloge : également appelé cycle d'oscillation. C'est l'inverse de la fréquence d'horloge (fréquence principale) et de la période minimale
Cycle de la machine : chaque étape du pipeline est appelée une opération de base, et le temps nécessaire pour terminer une opération de base est le cycle de la machine
Cycle d'instruction : temps nécessaire pour exécuter une instruction, généralement composé de plusieurs cycles machine
En plus des situations ci-dessus, il existe une autre raison courante pour la génération de bulles. Le temps nécessaire pour exécuter chaque instruction (cycle d’instruction) est différent. Lorsqu'une instruction simple est précédée d'une instruction complexe qui prend beaucoup de temps, l'instruction simple doit attendre l'instruction complexe. De plus, que se passe-t-il s'il y a une branche comme if dans le programme ? Ces situations empêcheront le pipeline de fonctionner à pleine capacité, ce qui entraînera une diminution relative des performances.
Face à un problème, les gens ont toujours tendance à introduire un mécanisme plus complexe pour résoudre le problème. La chaîne de montage à plusieurs étapes en est un exemple. La complexité peut refléter des améliorations technologiques, mais la « complexité » elle-même est un nouveau problème. C’est peut-être la raison pour laquelle les contradictions ne disparaîtront jamais et la technologie ne cessera jamais de progresser. Mais "plus nous apprenons, plus nous perdons pour Tao". Le mécanisme de plus en plus complexe aura toujours une avancée majeure à une certaine opportunité, mais peut-être que le moment n'est pas encore venu. Face au problème de la « bulle », le processeur a introduit une solution plus complexe : lorsqu'Intel a lancé le processeur Pentium Pro en 1995, il a ajouté un cœur dans le désordre (cœur OOO).
Noyau d'exécution dans le désordre (noyau OOO)En fait, l'idée de l'exécution dans le désordre est très simple : lorsque l'instruction suivante est bloquée, il suffit de trouver une autre instruction exécutable parmi les instructions suivantes. Mais y parvenir est assez compliqué. Tout d'abord, il est nécessaire de s'assurer que le résultat final du programme est cohérent avec l'exécution séquentielle, et en même temps, diverses dépendances de données doivent être identifiées. Pour obtenir l'effet souhaité, en plus de l'exécution parallèle, la granularité des instructions doit également être affinée davantage pour obtenir l'effet de n'utiliser aucune épaisseur pour obtenir l'effet souhaité. De cette manière, des « micro-opérations » (micro-opérations). , μ-ops) sont introduits le concept de. Au cours de l'étape de décodage du pipeline, les instructions d'assemblage sont ensuite démontées et le produit final est une série de micro-opérations.
Présentation du flux de traitement des instructions μ-ops après le noyau de traitement en panne. Les modules de différentes couleurs correspondent aux différentes étapes de traitement du pipeline de couleurs dans la première image.
Il n'y a pas beaucoup de changements dans l'étape Fetch. Dans l'étape Decode, quatre instructions peuvent être décodées en parallèle, et le produit final du décodage est les μ-ops mentionnés ci-dessus. La table d'alias de registre et le tampon de réorganisation suivants peuvent être considérés comme l'étape de prétraitement du noyau d'exécution dans le désordre.
Pour les micro-opérations exécutées en parallèle, ou les opérations exécutées dans le désordre, il est fort probable que le même registre soit lu et écrit en même temps. Par conséquent, au sein du processeur, les registres d'origine sont « alias » en tant que registres internes invisibles pour les ingénieurs logiciels, de sorte que les opérations initialement effectuées sur le même registre peuvent être effectuées sur des registres temporairement différents, indépendamment de la lecture et de l'écriture. autre (remarque : cela nécessite que les deux opérations n'aient aucune dépendance de données). Les opérandes des micro-opérations correspondantes ont également été transformés en registres d'alias temporaires, ce qui équivaut à une stratégie espace-temps, et en même temps, les micro-instructions sont traduites sur la base des registres d'alias.
Ensuite, la micro-opération entre dans le tampon de réorganisation. À ce stade, les micro-instructions sont prêtes. Ils sont déposés dans la Station de Réservation (RS) et exécutés en parallèle. Sur le diagramme, vous pouvez voir un certain nombre d'unités d'exécution (Port X). Chaque unité d'exécution effectue une tâche spécifique, telle que la lecture (Load), l'écriture (Store), le calcul d'entiers (ALU, SEE), etc. Chaque micro-instruction associée peut être exécutée une fois que les données dont elle a besoin sont prêtes. Bien que ces instructions longues et celles comportant des dépendances de données ne changent pas de leur propre point de vue, la surcharge de blocage qu'elles entraînent est compensée par le parallélisme et le désordre (avance) des points d'exécution ultérieurs est amorti et décomposé en. pièces, améliorant ainsi le débit global.
La magie du noyau d'exécution dans le désordre est qu'il peut maximiser l'efficacité de ce mécanisme, et depuis le monde extérieur, les instructions sont exécutées dans l'ordre. Les détails détaillés dépassent le cadre de cet article. Mais le cœur d'exécution dans le désordre connaît un tel succès que même sous une charge de travail importante, le cœur d'exécution dans le désordre du CPU qui introduit ce mécanisme restera inactif la plupart du temps et est loin d'être saturé. Par conséquent, un autre frontal (Front-end, comprenant Fetch et Decode) est introduit pour fournir des μ-ops au cœur. Du point de vue du système, il peut être résumé en deux cœurs de traitement, qui sont l'origine de l'Hyper-thread. N cœurs physiques et 2N cœurs logiques.
Une exécution dans le désordre n'obtient pas nécessairement l'effet d'une exécution de code séquentielle à 100 %. Parfois, les programmeurs doivent introduire des barrières de mémoire pour garantir l'ordre d'exécution.
Mais les choses complexes introduisent toujours de nouveaux problèmes. Cette fois, la contradiction a été transférée au stade Fetch. Comment choisir le bon chemin face à une branche ? Si la sélection d'instructions est erronée, l'ensemble du pipeline doit attendre que les instructions restantes soient exécutées, les effacer, puis recommencer à partir de la bonne position. Plus le niveau du pipeline est profond, plus les dégâts causés sont importants. Les articles suivants présenteront quelques méthodes d'optimisation au niveau de la programmation.
Présentation de l'auteur
Zhang Pan, ingénieur réseau à Yunshan, se concentre sur le développement et l'optimisation des performances des logiciels réseau x86. Il est profondément impliqué dans ONF/OPNFV/ONOS et dans d'autres organisations et communautés. Il a déjà été vice-président du groupe de travail de test de l'ONF.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!