13.08.2012 Views

Fonctionnement d'un ordinateur depuis zéro

Fonctionnement d'un ordinateur depuis zéro

Fonctionnement d'un ordinateur depuis zéro

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Partie 7 : Le parallélisme d'instruction et les processeurs modernes 331/343<br />

Les optimisations des accès mémoire<br />

Dans tout ce qu'on a vu précédemment, on a surtout parlé des instructions arithmétiques et des branchements. Le chapitre sur<br />

l’exécution Out Of Order nous a montré que l'on pouvait modifier l'ordre des instructions pour gagner en efficacité. Et avec le<br />

chapitre sur le renommage de registre, on a vu comment supprimer certaines dépendances entre instructions lorsque ces<br />

instructions utilisant des registres. Ainsi, si deux instructions réutilisaient le même registre, mais à des instants différents, on<br />

pouvait supprimer les dépendances WAR et WAW qui en résultait.<br />

Le seul problème, c'est que tout cela n'est valable que pour les instructions travaillant sur des registres. Si nos instructions<br />

doivent aller lire ou écrire dans la mémoire, le renommage de registre ne servira à rien ! Dans ce cas précis, on ne peut donc<br />

supprimer les dépendances WAR et WAW avec ce genre de techniques, ce qui diminue les possibilités d’exécution Out Of<br />

Order. Pour améliorer la situation, il a fallu trouver un moyen de limiter les effets de ces dépendances de données entre<br />

instructions d'accès mémoires. Pour ce faire, les concepteurs de processeurs et les chercheurs en architecture des <strong>ordinateur</strong>s<br />

ont inventés diverses techniques plus ou moins efficaces permettant de gérer ces dépendances entre instructions mémoires. Ces<br />

techniques sont ce qu'on appelle des techniques de Memory Disambiguation.<br />

Dépendances, le retour !<br />

Modifier l'ordre d’exécution des accès à la mémoire est une chose assez efficace en terme de performances. Par exemple, il vaut<br />

mieux effectuer les lectures le plus tôt possible. Il faut dire que ces lectures prennent un certain temps : accéder à une donnée ne<br />

se fait pas immédiatement. Cela peut prendre moins de 10 cycles d'horloge pour un accès au cache L1, mais peut facilement<br />

monter et atteindre des nombres à 2 chiffres pour les accès au L2, et trois chiffres pour les accès à la mémoire. Autant dire que ne<br />

rien faire durant cet accès au cache ou à la mémoire, et faire attendre les instructions suivant une instruction de lecture n'est pas<br />

une bonne chose. L'idéal serait d’exécuter des instructions indépendantes de l'accès en mémoire pendant qu'on attend que la<br />

donnée voulue soit lue. Pour ce faire, il suffit d’exécuter la lecture le plus précocement possible, et éxecuter des instructions<br />

indépendantes pendant ce temps.<br />

Seul problème : il faut que toutes les instructions ayant une dépendance avec cette lecture aient déjà finies de s’exécuter avant<br />

qu'on puisse lancer la lecture. Si on se trouve dans un tel cas, il se peut que l'on ne puisse démarrer notre lecture aussi tôt que<br />

prévu, parce qu'une instruction ayant une dépendance avec notre lecture n'est pas terminée : impossible de faire passer notre<br />

lecture avant celle-ci. Reste à savoir si ces dépendances sont monnaie courante.<br />

Utilité<br />

Dans le chapitre précédent, on a vu que ces dépendances naissaient lorsque l'on des instructions différentes veulent lire ou<br />

écrire dans des emplacements mémoire identiques. Dans les cas des registres, cela arrive très souvent : un processeur possède<br />

souvent une faible quantité de registres, qui doit donc être utilisée au mieux. Ainsi, nos compilateurs n’hésitent pas à réutiliser<br />

des registres dès que possible, et n'hésitent pas à écraser des données qui ne sont plus nécessaires pour stocker des résultats<br />

utiles. Réutiliser des registres le plus possible fait donc apparaitre de nombreuses dépendances WAR et WAW. Mais pour les<br />

accès mémoires, c'est autre chose. Accéder à la mémoire n'est pas rare, certes, mais réutiliser de la mémoire l'est. Il est en effet très<br />

rare qu'on doive lire ou écrire à des adresses identiques dans un cours laps de temps, et rares sont les dépendances WAR et<br />

WAW. Il faut dire que ces situations correspondent souvent à des données qui sont stockées temporairement sur la pile, à cause<br />

<strong>d'un</strong> manque de registres. On pourrait donc croire que chercher à supprimer les dépendances WAR et WAW pour les accès à la<br />

mémoire ne servirait que marginalement, et ne serait donc qu'un coup d'épée dans l'eau.<br />

Mais la situation est beaucoup plus compliquée que ce que cette présentation naïve vous l'a laissé entendre.<br />

De nouvelles dépendances<br />

Le seul truc, c'est que notre processeur ne peut pas toujours savoir si deux accès à la mémoire vont se faire au même endroit ou<br />

pas. Pour les instructions utilisant l'adressage absolu (l'adresse à laquelle lire ou écrire est stockée dans la suite de bits<br />

représentant notre instruction), il n'y a pas de problèmes. Mais le seul truc, c'est que ce n'est pas le cas pour d'autres modes<br />

d'adressages. Par exemple, il n'est pas rare que nos adresses soient stockées dans des registres. Il est en effet monnaie courante<br />

de ne pas connaitre à l'avance les adresses à laquelle lire ou écrire, et calculer des adresses est une chose commune de nos jours.<br />

Dans des cas pareils, il est impossible de savoir si deux accès à la mémoire se font à la même adresse ou pas.<br />

Bilan : deux accès à la mémoire peuvent être totalement indépendants, mais le processeur ne peut pas le savoir. Résultat : il est<br />

obligé de supposer par sécurité que ces deux accès sont dépendants, ce qui va limiter ses possibilités. Il ne pourra pas changer<br />

l'ordre de ses instructions pour gagner en efficacité. Et cela arrive très souvent : presque à chaque accès mémoire !<br />

Il faut noter que ce genre de situations arrive aussi dans un domaine assez éloigné. Certains compilateurs doivent faire face à un<br />

problème similaire dans certaines situations : dans certaines conditions, ils ne savent pas si deux adresses mémoire utilisées<br />

dans un programme sont différentes ou pas. Et dans ces conditions, ils doivent éviter de modifier l'ordre des accès à ces<br />

adresses, ce qui limite grandement les possibilités d’optimisation. C'est ce qu'on appelle le phénomènes d'aliasing des pointeurs.<br />

www.siteduzero.com

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!