![]()
Précédent : Simulation de processeurs et collecte
Remonter : Fondements scientifiques Suivant :
Mémoire
virtuellement partagée
Mots clés : hautes performances, compilation, hiérarchie mémoire, optimisation, transformation de code .
L'efficacité des mécanismes matériels pour l'exploitation de
la localité des références mémoire et du parallélisme, tant au
niveau multiprocesseurs qu'instruction (``Instruction Level
Parallelism''), dépend très fortement de la structure des
programmes. Cette structure est imposée par le programmeur mais
comporte des degrés de liberté que des techniques logicielles,
appelées optimisations de code, peuvent exploiter pour augmenter
la performance des applications. Ces optimisations de code sont
fondées sur des transformations de programmes, qui respectent la
sémantique des codes, mais réorganisent les calculs pour une
meilleure exploitation d'une architecture donnée.
Les transformations de code destinées à l'amélioration de performances peuvent intervenir à plusieurs étapes dans un processus de compilation. La figure 1 montre l'organisation générale d'un compilateur. Des transformations de code peuvent être effectuées aussi bien au niveau du code source qu'au niveau du code machine.
Les optimisations effectuées au niveau du code machine sont principalement les optimisations ``Peephole'', qui consistent à remplacer des séquences d'instructions par des séquences plus rapides, et surtout l'application des techniques d'ordonnancement de code. Cet ordonnancement doit prendre en compte les caractéristiques fines de l'architecture telles que le nombre de registres disponibles, l'usage des ressources des processeurs, etc.
Par exemple, pour l'exploitation du parallélisme
d'instructions au niveau logiciel, les méthodes les plus simples
se restreignent à l'exploitation du parallélisme entre les
instructions d'un même bloc de base
. Cependant, le nombre limité
d'instructions dans un bloc de base réduit l'efficacité de ce
type de techniques. En pratique, surtout dans le cas des boucles,
il faut extraire le parallélisme entre des instructions de
plusieurs blocs de base, par exemple en utilisant la technique du
pipeline logiciel. Cette technique, fondée sur l'exploitation du
parallélisme disponible entre les instructions d'itérations
différentes, consiste à segmenter le code des boucles d'une
manière similaire à celle utilisée par les pipelines
matériels.
Au niveau du code source, les transformations de programmes utilisent toutes les informations sémantiques disponibles tant au niveau du contrôle de flot que de l'usage des variables. A ce niveau, des réorganisations majeures du code peuvent être effectuées telles que par exemple le remplacement de l'appel d'une procédure par le corps de celle-ci ("inlining"). C'est aussi sur le code source que l'on peut appliquer les techniques de parallélisation automatique et les méthodes d'optimisation de la localité. Par exemple, la performance d'une hiérarchie mémoire dépend très fortement des caractéristiques de localité des accès aux données effectués par un programme. La prise en compte de la hiérarchie mémoire par un compilateur consiste à considérer les trois aspects fondamentaux suivants :
Il existe un nombre très important de transformations du code source pouvant être utilisées pour améliorer le comportement de la hiérarchie mémoire sur une application et/ou la paralléliser [BGS94]. La plupart de ces optimisations s'appliquent aux boucles. Parmi celles-ci on peut citer:
Ces transformations sont aujourd'hui relativement bien comprises individuellement. Le challenge est aujourd'hui de maîtriser l'interaction de toutes ces transformations et leurs impacts sur les performances.