Les processeurs

publicité
Titre_Itanium_XP
29/12/04
11:14
Page 1
Les processeurs
Itanium
Programmation
et optimisation
Smail
Jamel
© Groupe Eyrolles, 2005,
ISBN : 2-212-11536-9
Niar
Tayeb
Livre Itanium.book Page 55 Mardi, 28. décembre 2004 3:05 15
2
Exploiter EPIC
dans les applications
Ce chapitre propose de montrer comment les éléments architecturaux et le jeu d’instructions de l’Itanium (voir chapitre 1) sont utilisés pour produire des applications performantes. Plus précisément, il s’agit de donner les éléments permettant aux programmeurs
de faire une bonne utilisation des ressources de l’Itanium.
Comme il n’est pas question de programmer directement en code assembleur Itanium,
pour des raisons de simplification de la tâche du programmeur et de portabilité de code,
notre travail s’effectuera par le billet d'un certain nombre d'outils logiciels. Il s'agit plus
précisément des options et des directives de compilations. C’est donc à travers ces
éléments que nous vous expliquerons comment les applications pourront profiter des
ressources logicielles et matérielles de l’Itanium. Ainsi, nous montrerons comment le
compilateur C/C++ d’Intel v8.1 utilise la richesse des moyens de l’Itanium pour produire
un code performant.
Nous vous recommandons fortement l’utilisation des compilateurs signés Intel pour
générer vos codes binaires. Plusieurs raisons à cela dont la principale est la garantie
d’obtenir le meilleur niveau d’optimisation possible à un instant donné pour les architectures Intel dont l’EPIC. Par ailleurs, les compilateurs Intel n’ont pas pour vocation de
remplacer ceux déjà existants et bien installés dans la communauté des développeurs à
l’instar de GCC sous Linux ou de la série Visual de Microsoft. Ainsi, les compilateurs
Intel inter-opèrent avec ces derniers et sont généralement utilisés pour compiler et optimiser les quelques modules les plus exigeants en terme de performance.
Livre Itanium.book Page 56 Mardi, 28. décembre 2004 3:05 15
56
Les processeurs Itanium
Où trouver les compilateurs d’Intel ?
Le choix et l’adoption d’un compilateur ne sont pas anodins pour une entreprise. Il semble donc normal
qu’une équipe de développement souhaite prendre le temps nécessaire aux tests et à la réflexion. Vous
pouvez donc évaluer les outils Intel en général, et les compilateurs C/C++ et FORTRAN en particulier,
pendant une durée limitée de 30 jours. Ces moutures d’évaluation sont totalement fonctionnelles et
donnent accès au support en ligne de l’éditeur (Premier Support). Elles sont téléchargeables à l’adresse :
http://www.intel.com/software/products/compilers/index.htm. Vous devrez vous y enregistrer et recevrez en contre partie par e-mail une licence d’évaluation, non renouvelable, ainsi que l’adresse
du serveur FTP où vous pourrez entamer le téléchargement du ou des compilateurs. Une fois vos évaluations terminées, et si vous optez pour les compilateurs Intel, vous trouverez une liste de distributeurs
agrées pour effectuer vos achats. Signalons enfin que les outils Intel sont disponibles gratuitement pour
une utilisation non commerciale et individuelle sous Linux. Vous trouverez plus de détails sur la nature
exacte de la licence à l’adresse http://www.intel.com/software/products/noncom/.
Comment utiliser les compilateurs Intel ?
Loin de se substituer aux manuels d’utilisation livrés avec les compilateurs, nous souhaitons simplement
attirer votre attention ici sur les éléments complémentaires que vous devrez vous procurer afin de les
utiliser. Dans l’environnement Windows, vous devrez télécharger et installer la plate-forme SDK de Microsoft depuis le site MSDN de l’éditeur. Cette étape est impérative puisque le compilateur Intel n’est pas
autonome sous Windows. Cela signifie qu’il utilise les bibliothèques et l’éditeur de liens fournis par Microsoft.
Il existe de nombreux compilateurs pour les processeurs de la famille Itanium. Parmi
ceux-ci figurent : les compilateurs FORTRAN et C/C++ d’Intel pour systèmes Windows
et Linux, le compilateur C/C++ sous HP-UX de Hewlett-Packard, le compilateurs C/C++
de Microsoft livré dans la plate-forme SDK, la suite GCC sous Linux, l’ORC (Open
Research Compiler – ipf-orc.sourceforge.net), OpenIMPACT du groupe GELATO
(www.gelato.uiuc.edu), ou bien encore de nombreux projets d’optimisation fondés sur des
compilateurs existants tel que l’optimiseur C++ pour GCC 3.3 de l’Académie des Sciences
de Russie.
Précisons que l’objectif de ce chapitre n’est pas d’étudier la façon dont le compilateur
génère un code optimisé pour l’Itanium. Notre but consiste à présenter aux développeurs/
programmeurs les outils que nous offre le compilateur C/C++ d’Intel pour une utilisation
efficace. Ces outils sont représentés par une série de techniques de profilage et d’optimisation des logiciels que l’utilisateur pourra appeler lors de la compilation ou qu’il pourra
intégrer dans son application.
Nous verrons également que les méthodes d’optimisation de profilage présentées ici
pourront être utilisées aussi pour optimiser le code après compilation et profilage. En
effet, le compilateur est tout à fait capable d’appliquer une série de transformations et
d’optimisations (dépliage, fusion, pipeline logiciel, etc.). Néanmoins, il a été constaté
dans la pratique, que lorsque l’utilisateur intègre ces optimisations directement dans son
code (par des pragmas par exemple), cela permet d’obtenir un gain de temps au niveau de
la compilation et donne la possibilité au compilateur de se concentrer sur d’autres pistes
d’optimisations. Ainsi, dans la mesure où le développeur a le temps d’analyser son
Livre Itanium.book Page 57 Mardi, 28. décembre 2004 3:05 15
Exploiter EPIC dans les applications
CHAPITRE 2
application après compilation et de l’optimiser (par transformation ou par l’ajout de
pragmas), le compilateur pourra dans ce cas créer un code encore plus efficace.
Pour illustrer ces techniques, nous utiliserons des petits programmes lorsque cela est
possible. Certes, ils sont, loin des applications réelles mais ils ont l’avantage d’être clairs
et concis. Le lecteur trouvera dans les chapitres 5 et 6 des techniques d’optimisation sur
des applications réelles.
L’ensemble des tests que nous présentons dans ce chapitre a été réalisé sur un système
DELL PowerEdge 3250 équipé d’un bi-processeur Itanium 2 à 1,4 GHz. Ce dernier est
doté d’une mémoire cache L1 de 32 Ko, d’une mémoire cache L2 de 256 Ko et d’une
mémoire cache L3 de 3 Mo. Conscients que la lecture d’un code assembleur n’est pas
une tâche facile, nous avons d’une part commenté le code Itanium issu de la compilation
et d’autre part, nous avons supprimé du code les instructions Itanium qui ne sont pas
essentielles pour comprendre les facilités offertes aux logiciels par EPIC.
Restructuration des boucles
Comme nous l’avons vu au chapitre 1, l’architecture Itanium comprend plusieurs
niveaux de mémoires cache. L’objectif de cette hiérarchie de mémoires cache est de
réduire la différence des latences entre le processeur d’une part et la mémoire externe
d’autre part. Au niveau du processeur, jusqu’à 4 instructions mémoire (correspondants
aux 4 ports M) peuvent être exécutées par cycle. Deux instructions parmi les quatre
doivent être des instructions de chargement. Les deux autres doivent correspondre à des
instructions de stockage (store) en mémoire. Malheureusement, la mémoire externe ne
peut pas suivre une telle cadence. Même en utilisant les mémoires DRAM les plus rapides, on arrive à des temps d’accès supérieurs à 50 cycles. Cette latence au niveau des
accès mémoire aurait pu être utilisée pour exécuter jusqu’à 50*6 instructions (la valeur 6
correspond à deux bundles de 3 instructions chacun), soit 300 instructions. Comme, en
plus, les instructions d’accès mémoire peuvent représenter jusqu’à 30 % des instructions
d’une application, la quantité d’instructions pouvant être exécutées pendant le temps
d’accès aux données au niveau des DRAM peut être très importante.
De ce fait, il est intéressant de disposer des instructions et des données sur lesquelles
travaille le processeur dans les mémoires cache, qui travaillent à des vitesses plus grandes que celle de la mémoire externe. Le but de cette section est de montrer comment il est
possible d’utiliser les optimisations que le compilateur C/C++ d’Intel offre à nos applications
afin de mieux profiter de ces mémoires cache.
En effet, la grande richesse des ressources disponibles au niveau du processeur, (parmi
lesquelles, les prédicats, le grand ensemble de registres, la spéculation sur les données,
les systèmes de rotation des registres, etc.) permet d’implémenter facilement les techniques classiques et habituelles d’optimisation de code (comme les transformations de
boucles) et également de réaliser efficacement de nouvelles optimisations (comme le
dépliage spéculatif des boucles, le préchargement, etc.).
57
Livre Itanium.book Page 58 Mardi, 28. décembre 2004 3:05 15
58
Les processeurs Itanium
Dans cette première section, nous allons présenter les optimisations de boucle proposées
par le compilateur C/C++ d’Intel. Nous nous limiterons à deux facettes de ces optimisations : les restructurations des boucles et la gestion des opérations de préchargement. Le
lecteur intéressé par une présentation plus détaillée pourra trouver dans [UgLx04]
[UgWn04] un supplément d’informations.
Optimisation du code par transformation des boucles
D’après les statistiques obtenues sur plusieurs applications, un processeur passe en général
environ 90 % de son temps sur 10 % de l’application [HennPatte03]. En d’autres termes, il y
a des parties qui sont plus souvent exécutées que d’autres dans la quasi-majorité des applications. Ces parties sont en général représentées par des boucles. Il est donc intéressant de doter
les compilateurs de moyens permettant une plus grande optimisation des boucles. C’est
pourquoi une grande attention est donnée aux boucles par les concepteurs de compilateurs.
La plupart des compilateurs modernes réalisent des transformations de boucles. Les
techniques comme : le dépliage des boucles, la permutation, l’échange de boucles, la
distribution des boucles et l’inversion des boucles, par exemple, sont présents aussi dans
les autres compilateurs pour les autres processeurs. La nouveauté avec Itanium est qu’un
certain nombre de ces optimisations est facilement réalisable ; ce qui offre un gain de
performance important pour les applications.
Remplacement des accès mémoire par des scalaires
L’une des premières optimisations que les compilateurs réalisent en général consiste à
supprimer l’évaluation redondante de la même expression. Cette technique est communément appelée PRE (Partial Redundancy Elimination). Dans l’Itanium, elle a été étendue pour éliminer aussi dans les boucles un certain nombre d’instructions de chargement
et de stockage redondantes.
Le remplacement de ces références mémoire par des scalaires créés par le compilateur
consiste à remplacer dans une boucle, une instruction mémoire (load) par une référence à un
registre. Cette optimisation est réalisée par l’option /Qscalar_rep du compilateur C/C++.
Pour désactiver cette option, il faut mettre l’option /Qscalar_rep-. Par défaut cette option est
activée.
Même si les options /O1 et /O2 permettent de réaliser le remplacement de références
mémoires par des références registres, l’utilisation conjointe des options /O3 et /Qscalar_rep
donne l’occasion au compilateur de faire un remplacement plus agressif des expressions
redondantes et des accès mémoires inutiles. À titre d’exemple, avec /O3 et /Qscalar_rep,
le compilateur ira chercher des remplacements de référence mémoire par des registres
même en présence de dépendance entre itérations au prix, bien sûr, d’un temps de compilation plus grand.
Le code suivant donne un exemple de boucle où le remplacement par des scalaires est réalisé.
// version d’origine en C
for (i=1, i<n ;i++) { // ligne 7
Livre Itanium.book Page 59 Mardi, 28. décembre 2004 3:05 15
Exploiter EPIC dans les applications
CHAPITRE 2
a[i] = a[i-1]+1 ; // ligne 8
b[i] = a[i]+a[i-1] ; //ligne 9
}
Voici maintenant le code après transformation :
//version
t2 = a[0]
for (i=1,
t1 =
a[i]
b[i]
t2 =
}
optimisée en C
;
i<n ;i++) {
t2+1;
= t1 ;
= t1+t2 ;
t1 ;
Comme on peut le voir, la version transformée ne nécessite aucune instruction de lecture
mémoire dans la boucle, alors que la première version nécessite au moins deux instructions de lecture mémoire à chaque itération (pour a[i] et a[i-1]). En effet, la transformation profite du fait que tous les éléments des deux vecteurs a et b sont calculés puis
stockés dans la boucle. Par conséquent, mis à part la valeur de a[0], les valeurs des
éléments du vecteur a avant la boucle n’ont aucune influence sur les valeurs de a et b
après la boucle.
Le seul inconvénient de cette deuxième version est le nombre d’instructions nécessaires
pour coder la boucle. Lorsque le corps de celle-ci est grand et/ou lorsque cette optimisation est appliquée à plusieurs variables dans la même boucle, cela risque d’augmenter le
nombre de défauts dans les mémoires caches des instructions. Pour palier ce handicap,
l’Itanium propose d’utiliser le système des registres rotatifs de RSE pour garder un corps
de boucle de taille relativement réduite, en supprimant les instructions de transferts entre
registres. Le code suivant donne la version Itanium du code de la boucle précédente.
Dans ce code, t1 et t2 sont représentés (respectivement) par r32 et r33. Comme l’instruction t2=t1 est ici intégrée implicitement dans l’instruction br.ctop b1 (voir chapitre 1,
section « Pipeline logiciel »), cela permet d’économiser une instruction dans le code.
// code Itanium avec optimisation
mov ar.lc = 99
// initialisation de lc par n-1, on suppose que n=100
b1 :
add r32 = r33, 1
// t1 = t2 +1
st8[r2] = r32, 8
// a[i] = t1
add r4 = r32, r33 // r4 = t1+t2
st8 [r3] = r4, 8
// b[i] = r4
br.ctop b1 // rotation (r33=r32) et aller à b1 si lc > 0.
Le compilateur Intel donne un code beaucoup plus long que celui qu’on vient de présenter. En effet l’option /O3 fait activer une autre optimisation, que l’on va présenter dans la
suite de ce paragraphe. Celle-ci concerne le dépliage de boucles. Ainsi le compilateur
que nous avons utilisé réalise un dépliage de 8 itérations.
59
Téléchargement