|

|
Tout le monde se presse
pour compiler ses programmes pour IA-64. Est-ce
judicieux ?
Le RISC a pris sous son aile la conception
de la quasi-totalité des microprocesseurs de nos
ordinateurs depuis le jour où un ARM à 8MHz a
tenu la comparaison avec le 386 le plus puissant du moment,
le modèle 20MHz. Même si certains clament la
victoire du CISC en faisant remarquer à juste titre
que les microprocesseurs les plus vendus sont les
héritiers de l'antique 8086, les techniques RISC n'en
sont pas moins à la base du coeur des tout derniers
x86.
Or, EPIC implique un changement
plutôt radical de la manière de coder un
programme en assembleur. Ces changements sont-ils judicieux
et révolutionnaires ? Voici un avis subjectif,
justifié par des remarques se voulant
objectives.
Quelles
instructions exécuter ?
IA-64 introduit des
instructions à prédicat, qui ne sont
validées que si le prédicat associé est
vrai. Cela évite certaines bulles causées par
les branchements conditionnels non résolus, mais
même si elles sont invalidées, les instructions
occupent des unités de traitement, d'où une
sous-utilisation des ressources. De plus les instructions
annulées prennent de la place dans le code, qui
augmente en taille.
Les implémentations réelles
utiliseront quand même de la prédiction
dynamique de branchement : tout d'abord car la
prédiction statique est en général
moins performante que la dynamique (elle est la moyenne
d'une situation globale), ensuite parce qu'utiliser
uniquement la prédication est impensable (pensez-y
;-). Ce n'est donc pas là que l'architecture sera
simplifiée. En fait elle l'est apparemment sur
l'Itanium, qui comprend une unité de
prédiction largement moins complexe que l'état
de l'art actuel. Seulement elle sera probablement aussi
notablement moins performante...
Les
instructions atypiques
Pour éviter les pénalités dues au
chargement de données à partir de la
mémoire (crucial sur un processeur dont
l'ordonnancement des instructions repose
intégralement sur le compilateur), IA-64 utilise des
instructions de chargement anticipé, et des
instructions de confirmation de ce chargement. D'où
un doublement de la taille du code, et on pourra faire
remarquer à juste titre que d'autres moyens peuvent
être utilisés pour résoudre ce
problème, comme le préchargement des lignes de
cache par exemple.
IA-64 peut dérouler les boucles et
les pipeliner par matériel, ce qui évite la
multiplication explosive de la taille du code lorsque ces
techniques sont employées par logiciel. Oui, mais
(car il y a un mais)... Souvent, les boucles cruciales
à exécuter sont de petite taille et tiennent
très facilement dans les plus petits caches de
premier niveau : ce n'est donc pas grave d'en augmenter
la taille. De plus, si on les pipeline, c'est qu'elles
travaillent sur de grandes quantités de
données en mémoire, donc que le
côté pénalisant n'est pas le code
à exécuter mais la bande passante...
L'exécution des instructions est en
général contrainte par un prédicat.
Nous l'avons vu, cela permet d'éviter les bulles dues
aux branchements non résolus. Mais cela remplace une
dépendance de branchement par une dépendance
de donnée, beaucoup plus difficile à
prédire... Remarquons toutefois que si l'on veut par
exemple exécuter a=b+c
ou a=c-d en fonction du résultat d'un test,
on peut très bien calculer b+c et
c-d dans des registres tampons et assigner le
résultat à a avec
un MOVE conditionnel (instruction présente sur Alpha,
mais qui manque au jeu entier du PowerPC).
Étonnant...
L'utilisation de bundles contenant des instructions
garanties comme pouvant s'exécuter en
parallèle simplifie la conception interne du
microprocesseur. Conséquence des choix architecturaux
réalisés, les bundles sont à la
fois : de taille imposante (128 bits pour 3
instructions au lieu de 96 pour un RISC classique, en partie
à cause de l'utilisation de 7 bits pour
désigner chaque registre au lieu de 5, et de 18 bits
pour les bits de prédicat des 3 instructions) et
relativement rigides (le nombre d'instructions à
exécuter en parallèle doit sauf exception
être un multiple de 3, et un bundle ne peut contenir
qu'une instruction de calcul en virgule flottante sur les 3
instructions).
Plus étonnante encore est la
présence d'instructions relativement complexes. Par
exemple une instruction de bouclage relative à un
compteur (comme sur PowerPC). Des instructions de ce type
sont appréciables et indéniablement utiles,
mais elles nécessitent de nombreux ports de lecture
et d'écriture dans les bancs de registres, ports
quasiment tout le temps inutilisés par les autres
instructions, donc peu rentables... Ou encore des
instructions de sauvegarde des registres par le processeur
lui-même (présence de micro-code ?). Ou
enfin des instructions de lecture mémoire avec mise
à jour du registre d'index (une nouvelle fois comme
sur PowerPC), ce qui implique deux ports d'écriture
(un pour la donnée lue, un pour le registre mis
à jour), voir la critique ci-avant.
Pour en rajouter sur les instructions
d'accès mémoire, notons qu'elles ne comportent
pas... d'offset ! C'est uniquement le contenu d'un
registre qui indique l'adresse à lire, d'où
des contorsions peu sympathiques pour accéder aux
divers champs d'une structure de données...
Rien à
ajouter ?
Si... Quelques mots encore.
On peut être impressionné par le nombre de
registres (128), mais ils servent en premier lieu à
contrecarrer les effets des latences. En effet, les
processeurs RISC règlent certaines dépendances
de données en renommant les registres. Il n'est pas
rare qu'un processeur utilise en interne près de 80
registres, alors que seulement 32 (voire moins) sont
disponibles dans le modèle de programmation. Dans
notre cas, c'est le compilateur qui effectue à peu
près la même opération, en utilisant les
nombreux registres disponibles.
Notons aussi que le compilateur a une
importance cruciale pour la performance d'Itanium et de ses
descendants : si ce ne sont pas des RISC (Reduced
Instruction Set Complexity), ce sont tout de même des
RISC (Repose Intégralement Sur le Compilateur). Mais
nombre de ces techniques peuvent aussi profiter aux
processeurs plus classiques.
De plus, tous ceux qui y ont touché
disent qu'Itanium chauffe beaucoup... Fini, n'en jetons
plus.
Conclusion
largement subjective...
Les solutions
apportées semblent audacieuses. Mais rien ne prouve
que la performance moyenne en sera améliorée,
pas plus que la performance crête. En effet, à
ce jour (30/12/1999) Itanium est annoncé comme
délivrant à sa sortie 6 gigaflops. Comportant
deux unités de calcul en virgule flottante, chacune
effectuant la multiplication-addition fusionnée sur
deux nombres en virgule flottante simple précision en
même temps, on peut en déduire une
fréquence de fontionnement de 750MHz. Ce qui, milieu
2000, sera un peu faible, vu qu'Alpha et Athlon atteignent
déjà cette fréquence, et annoncent le
GigaHertz, ou plus, à la même date. Toujours
à propos de cette puissance de calcul, si
l'algorithme ne permet pas d'utiliser la
multiplication-addition fusionnée et nécessite
la double précision, Itanium fournira...à peu
près la même performance crête que les
actuels Athlon et Alpha 750 !
Cependant, à la sortie d'Itanium,
nous devons nous attendre à d'importants facteurs
d'accélération. Rien que l'utilisation du 64
bits pourra donner un important coup de fouet à
nombre d'applications, comme cela a été le cas
il y a quelques années avec Alpha (qui n'a pourtant
pas révolutionné le monde de la
micro-informatique). Les indices moyens donnés par
les séries de benchmarks pourront eux aussi
être boostés, sans pour autant indiquer quels
sont les programmes les plus bénéficiaires ni
les moins bénéficiaires.
Difficile aujourd'hui de dire comment
nous, utilisateurs et programmeurs,
bénéficierons de cette architecture. Sans
avantage indéniable, avec une architecture qui
s'offre quelques compromis et des techniques de compilation
à la fois novatrices et déjà
éprouvées (certes sous des formes
légèrement différentes, mais qui
avaient été rejetées), il ne nous reste
probablement qu'à attendre la fin 2000 pour
connaître la vérité, tout en continuant
à profiter d'ici là des technologies
disponibles.
|
|