web-dev-qa-db-fra.com

Comment fonctionne la prédiction de branche, si vous devez toujours vérifier les conditions?

Je lisais la réponse populaire sur la prédiction de branche de https://stackoverflow.com/q/11227809/55569 , et il y a quelque chose qui me déroute:

  • Si vous avez bien deviné, cela continue.
  • Si vous vous trompez, le capitaine s'arrête, recule et vous crie dessus pour actionner l'interrupteur. Ensuite, il peut redémarrer sur l'autre chemin.

Si vous devinez à chaque fois, le train n'aura jamais à s'arrêter.

Si vous vous trompez trop souvent, le train passera beaucoup de temps à s'arrêter, à reculer et à redémarrer.

Mais c'est ce que je ne comprends pas: pour savoir si votre supposition était bonne ou mauvaise, vous devez faire une vérification de condition de toute façon. Alors, comment fonctionne la prédiction de branche, si vous effectuez toujours la même vérification conditionnelle?

Ce que j'essaie de dire, c'est que la prédiction de branche n'est pas exactement la même chose que de ne pas avoir de prédiction de branche du tout parce que vous effectuez les mêmes vérifications conditionnelles de toute façon? (évidemment je me trompe, mais je ne comprends pas)

33
Omega

Bien sûr, la condition est vérifiée à chaque fois. Mais au moment où il est vérifié, il est loin dans le pipeline du processeur. Entre-temps, d'autres instructions sont également entrées dans le pipeline et sont à divers stades d'exécution.

Habituellement, une condition est immédiatement suivie d'une instruction de branchement conditionnel, qui se branche si la condition est évaluée à VRAI, ou échoue si la condition est évaluée à FAUX. Cela signifie qu'il existe deux flux d'instructions différents qui peuvent être chargés dans le pipeline après l'instruction de condition et l'instruction de branchement, selon que la condition est évaluée à VRAI ou FAUX. Malheureusement, immédiatement après le chargement de l'instruction de condition et de l'instruction de branchement, le processeur ne sait pas encore à quoi la condition va être évaluée, mais il doit toujours continuer à charger des éléments dans le pipeline. Il choisit donc l'un des deux ensembles d'instructions en fonction d'une supposition quant à la condition à laquelle la condition sera évaluée.

Plus tard, alors que l'instruction de condition remonte le pipeline, il est temps de l'évaluer. À ce moment-là, le CPU découvre si sa supposition était bonne ou mauvaise.

Si la supposition s'avère juste, alors la branche est allée au bon endroit et les bonnes instructions ont été chargées dans le pipeline. S'il s'avère que la supposition était erronée, alors toutes les instructions qui ont été chargées dans le pipeline après que l'instruction de branchement conditionnel était erronée, elles doivent être rejetées et la récupération des instructions doit recommencer au bon endroit.

Amendement

En réponse au commentaire de StarWeaver, pour donner une idée de ce que le CPU doit faire pour exécuter une seule instruction:

Considérez quelque chose d'aussi simple que MOV AX,[SI+10] que nous, humains, considérons naïvement comme "chargez AX avec la Parole à SI plus 10". En gros, le CPU doit:

  1. émettre le contenu du PC (le "registre de compteur de programme") vers le bus d'adresse;
  2. lire l'opcode d'instruction du bus de données;
  3. incrémenter PC;
  4. décoder l'opcode pour savoir quoi en faire;
  5. émettre le contenu du PC vers le bus d'adresse;
  6. lire l'opérande d'instruction (dans ce cas 10) à partir du bus de données;
  7. incrémenter PC;
  8. fournir l'opérande et SI à l'additionneur;
  9. émettre le résultat de l'additionneur sur le bus d'adresse;
  10. lire AX depuis le bus de données.

C'est un énorme 10 étapes. Certaines de ces étapes seront optimisées même dans les processeurs non pipelinés, par exemple le CPU incrémentera presque toujours le PC en parallèle avec l'étape suivante, ce qui est une chose facile à faire car le PC est un registre très très spécial qui est jamais utilisé pour un autre travail, il n'y a donc aucune possibilité de conflit entre les différentes parties du CPU pour accéder à ce registre particulier. Mais encore, il nous reste 8 étapes pour une instruction aussi simple, et notez que je suppose déjà un certain degré de sophistication au nom du CPU, par exemple je suppose qu'il n'y aura pas besoin d'une étape supplémentaire entière pour le additionneur pour effectuer réellement l'addition avant que le résultat puisse être lu à partir de celui-ci, et je suppose que la sortie de l'additionneur peut être envoyée directement au bus d'adresse sans avoir à être stockée dans un registre d'adressage interne intermédiaire.

Maintenant, considérez qu'il existe des modes d'adressage plus compliqués, comme MOV AX, [DX+SI*4+10], et des instructions encore plus compliquées, comme MUL AX, operand qui effectuent réellement des boucles à l'intérieur du CPU pour calculer leur résultat.

Donc, mon point ici est que la métaphore du "niveau atomique" est loin d'être appropriée au niveau d'instruction CPU. Il peut convenir au niveau de l'étape du pipeline, si vous ne voulez pas aller trop loin jusqu'au niveau de la porte logique réelle.

19
Mike Nakis

Pensez-y comme un road trip sans GPS. Vous arrivez à une intersection et pensez que vous devez tourner, mais vous n'êtes pas complètement sûr. Vous prenez donc le virage, mais demandez à votre passager de vérifier la carte. Peut-être êtes-vous à trois milles sur la route au moment où vous avez fini de vous disputer sur votre position. Si vous aviez raison, vous êtes à trois milles plus loin que vous ne l'auriez été si vous vous étiez arrêté et argumenté avant de tourner. Si vous vous trompiez, vous devez vous retourner.

Les pipelines CPU fonctionnent de la même manière. Au moment où ils peuvent vérifier l'état, ils sont déjà en chemin. La différence est qu'ils n'ont pas à reculer de trois milles, ils perdent juste l'avance. Cela signifie qu'il n'y a aucun mal à essayer.

31
Karl Bielefeldt

D'après ma compréhension, la prédiction de branche est plus utile lorsque la condition que vous devez vérifier nécessite le résultat de quelque chose qui est cher ou encore en cours, et vous seriez autrement en train de tourner les pouces en attendant la valeur pour évaluer la condition.

Avec des choses comme l'exécution dans le désordre, vous pouvez utiliser la prédiction de branche pour commencer à remplir des espaces vides dans le pipeline que le CPU ne pourrait pas utiliser autrement. Dans une situation où il n'y a, pour une raison quelconque, aucun cycle inactif dans le pipeline, alors oui, il n'y a pas de gain dans la prédiction de branche.

Mais la clé ici est que le CPU commence le travail pour l'une des branches prédites car il ne peut pas évaluer la condition elle-même pour le moment.

2
Dogs

Forme courte:

Certains processeurs peuvent commencer à travailler sur une nouvelle instruction avant de terminer l'ancienne. Ce sont les CPU qui utilisent la prédiction de branche.

Un exemple de pseudocode:

int globalVariable;
int Read(int* readThis, int* readThat)
{
    if ((globalVariable*globalVariable % 17) < 5)
       return *readThis;
    else
       return *readThat;
}

Le code ci-dessus vérifie une condition et en fonction du résultat, il doit renvoyer la valeur stockée à l'emplacement de mémoire addThis ou la valeur stockée à readThat. Si la prédiction de branche prévoit que la condition sera true, le CPU lira déjà la valeur stockée à l'emplacement de mémoire addThis tout en effectuant le calcul nécessaire pour évaluer l'instruction if. Ceci est un exemple simplifié.

1
Peter

Oui, la condition est vérifiée dans les deux cas. Mais l'avantage de la prédiction de branche est que vous pouvez travailler au lieu d'attendre le résultat du contrôle de condition.

Disons que vous devez écrire un essai et qu'il peut s'agir du sujet A ou du sujet B. D'après les essais précédents, votre enseignant aime mieux le sujet A que B et le choisit plus souvent. Au lieu d'attendre sa décision, vous pouvez commencer à rédiger l'essai sur le premier sujet. Maintenant, il y a deux résultats possibles:

  1. Vous avez commencé votre essai sur le mauvais sujet et devez abandonner ce que vous avez écrit jusqu'à présent. Vous devez commencer à écrire sur l'autre sujet et c'est le même effort de temps que si vous aviez attendu.
  2. Vous avez bien deviné et vous avez déjà fait du travail.

Les processeurs modernes tournent au ralenti la plupart du temps car ils attendent des réponses IO ou le résultat d'autres calculs. Ce temps peut être utilisé pour effectuer des travaux futurs.

Même si vous devez ignorer ce que vous faites en cette période d'inactivité, il est plus probable qu'il soit plus efficace si vous avez la possibilité de deviner la voie que le programme choisira. Et les processeurs modernes ont cette capacité.

1
Otomo