Fondements scientifiques
Mots clés : évaluation partielle,
spécialisation, transformation de programmes, systèmes
adaptatifs, génie logiciel .
- Évaluation partielle
- transformation de programmes qui a pour but de
spécialiser un programme en fonction de certaines de ses
données d'entrée.
L'adaptabilité est devenue une caractéristique
incontournable dans la conception des nouveaux logiciels pour
leur permettre de répondre à des besoins fondamentaux tels
que :
- l'évolution et l'hétérogénéité des matériels
informatiques, ainsi que la prise en compte de leurs
caractéristiques de bas niveau ;
- la généralité sans cesse croissante des problèmes que
doivent traiter les logiciels pour contrebalancer leur coût
de développement ;
- le besoin d'intégration avec d'autres composants
logiciels pour constituer des systèmes informatiques
complets.
Des techniques de conception de logiciels adaptatifs
existent déjà ; elles consistent généralement à
structurer un logiciel de telle sorte qu'il puisse évoluer en
fonction de son contexte d'utilisation. Cette structuration
prend, traditionnellement, la forme de modules et de
couches logicielles. L'évolution de ces techniques
s'est traduite par un réel engouement pour les langages à
objets dont l'un des objectifs principaux est d'offrir des
mécanismes d'organisation et de généralisation de composants
logiciels. Plus récemment, des approches reposant sur la
notion de bus logiciel ont été proposées pour permettre la
composition de composants logiciels indépendants, mais dont
l'interface est spécifiée. Toutefois, les approches
existantes sont incomplètes car, bien qu'elles prennent en
compte les aspects conceptuels d'un logiciel, elles négligent
ses aspects relatifs à l'implémentation. Par faute de
méthodes et d'outils adéquats, l'adaptabilité se traduit
souvent par l'introduction, dans la mise en oeuvre, de
mécanismes tels que l'interprétation (de paramètres ou d'un
état global), la protection des données et du code, et la
copie de données entre différentes couches logicielles. Ces
mécanismes complexifient les algorithmes et entraînent une
inefficacité importante qui conduit bien souvent à
spécialiser manuellement un logiciel pour un contexte
d'utilisation donné afin d'obtenir des performances
acceptables. Cette spécialisation manuelle est bien
évidemment fastidieuse et source d'erreurs. De plus, elle
multiplie les versions d'un même logiciel entraînant du même
coup des problèmes de maintenance. Plus généralement, elle
annule les efforts d'adaptabilité du logiciel déployés lors
de sa conception. Notre objectif est d'étudier la
généralisation et la systématisation des techniques de
spécialisation et leur utilisation pour des applications de
taille réelle. L'évaluation partielle est à la base de notre
approche de conception de programmes et systèmes adaptatifs,
nous en présentons maintenant ses aspects fondamentaux.
L'évaluation partielle a pour but de
spécialiser un programme en fonction de certaines de ses
données d'entrée. Cette transformation de programmes préserve
la sémantique initiale dans la mesure où le programme
spécialisé, appliqué aux données manquantes, produit le
même résultat que le programme original appliqué à toutes les
données. Comme on aime à le souligner, la calculabilité de
l'évaluation partielle repose sur le théorème Smn de Kleene
[JSS89,Kle52]. À la différence d'une stratégie de
transformation de programmes générale à la Burstall et
Darlington [BD77], l'évaluation partielle a pour unique
objectif la spécialisation de programmes. Un évaluateur
partiel consiste en un ensemble réduit de règles de
transformation de programmes visant à évaluer les expressions
qui manipulent des données disponibles et à reconstruire les
expressions dépendant des données manquantes. Bien que simple
dans son principe, la notion de spécialisation s'applique à
une vaste classe de problèmes. En effet, l'évaluation
partielle a été utilisée pour des applications aussi variées
que la génération de compilateurs à partir
d'interprètes [JSS89], l'optimisation de programmes
numériques [Ber90], le filtrage [CD89] et l'instrumentation de
programmes [KHC91].
Pour présenter l'évaluation partielle,
il est important d'établir une distinction entre un programme
et la fonction (c'est-à-dire l'objet mathématique) que ce
programme dénote. Pour ce faire nous utilisons la convention
suivante : lorsque le nom d'une variable apparaît en
majuscule, cette variable dénote un programme, sinon elle
dénote une fonction. Nous ne définissons pas la
correspondance entre un programme et la fonction qu'il
dénote ; nous supposons que cette correspondance est
donnée par la sémantique formelle du langage. Nous supposons
de plus qu'il existe un évaluateur eval tel
que :
eval (P, d )= p d
Etant donné un programme P à deux entrées et une valeur
v, un évaluateur partiel est
un programme, noté PE,
calculant le programme résiduel Pv
Pv = eval (PE,(P,
v))
tel que, lorsque le programme résiduel est appliqué à
la donnée manquante w
eval (
Pv,
w)
eval
(
P,(
v,
w))
le programme original et le programme spécialisé
produisent le même résultat. D'un point de vue fonctionnel,
ceci peut être écrit comme suit :
pv(
w)
p(
v,
w)
où Pv =
pe(
P,
v)
Les données disponibles pendant l'évaluation partielle
sont dites statiques (la donnée v). Les données manquantes sont dites
dynamiques (la donnée w) [JSS89]. On dit que Pv est la version spécialisée
de P en fonction de
v. Il est également possible
d'optimiser le processus même d'évaluation partielle par
auto-application de l'évaluateur partiel.
On distingue communément deux stratégies
d'évaluation partielle : en ligne et hors
ligne. La première stratégie consiste à déterminer le
traitement du programme au fur et à mesure de la phase
d'évaluation partielle. Les évaluateurs partiels basés sur ce
principe ont l'avantage de manipuler des valeurs concrètes,
et donc, peuvent déterminer précisément le traitement de
chaque expression d'un programme. Toutefois, ce processus est
coûteux : l'évaluateur partiel doit analyser le contexte
du calcul (c'est-à-dire les données disponibles) pour
sélectionner la transformation de programmes appropriée.
Cette opération est effectuée de façon répétitive dans le cas
de fonctions récursives, par exemple. Il n'est donc pas
surprenant de constater que les performances d'un évaluateur
partiel en ligne se dégradent rapidement lorsque de nouvelles
transformations de programmes sont introduites. La deuxième
stratégie d'évaluation partielle comporte deux phases :
une analyse de temps de liaison et une phase de
spécialisation [JSS89]. Étant donné un programme et une
description de ses entrées (statique/dynamique), l'analyse de
temps de liaison détermine les expressions qui peuvent être
évaluées lors de la phase d'évaluation partielle et celles
qui doivent être reconstruites : les premières sont
dites statiques, les autres dynamiques. Les informations de
temps de liaison sont valides tant que la description des
entrées du programme reste inchangée. Les expressions
statiques et dynamiques étant connues à l'avance, la phase de
spécialisation est plus efficace. Toutefois, l'analyse de
temps de liaison, manipulant des valeurs abstraites, permet
d'effectuer certaines approximations. Ainsi, le degré de
spécialisation d'une stratégie hors ligne peut être moindre
que celui d'une stratégie en ligne. L'activité importante qui
s'est développée dans le domaine de l'évaluation partielle a
conduit à la réalisation de nombreux prototypes pour une
variété de langages de programmation tels que
Scheme [Bon90,Con93b], C [And94] et Pascal [Mey91].
Traditionnellement, l'évaluation
partielle est une transformation effectuée sur le texte d'un
programme. De ce fait, elle intervient à la compilation et ne
peut exploiter que les valeurs disponibles à ce stade. Nous
avons développé un cadre de travail général permettant de
réaliser la spécialisation de programmes impératifs à
l'exécution [5]. Le processus de spécialisation que nous
avons conçu a pour point de départ un programme annoté
d'actions. Ces actions décrivent les transformations à
effectuer sur chaque construction du programme à spécialiser
et, de fait, peuvent être vues comme une description de
l'ensemble des programmes qui peuvent être produits par
spécialisation. Notons que ce point de départ n'est pas
spécifique à la spécialisation à l'exécution ; dans
notre approche, les actions sont également utilisées pour
guider la spécialisation à la compilation. À partir d'un
programme annoté d'actions, nous produisons automatiquement à
la compilation des fragments de code source. Ces fragments de
code source sont incomplets dans la mesure où les invariants
ne sont connus qu'à l'exécution. Ils sont transformés de
manière à pouvoir être traités par un compilateur standard. À
l'exécution, il ne reste plus qu'à sélectionner et copier
certains de ces fragments de code, insérer les valeurs
dynamiques et reloger certains sauts pour obtenir une
spécialisation donnée. Ces opérations sont simples et
permettent donc un processus de spécialisation très efficace
qui ne nécessite qu'un nombre limité d'exécutions du
programme spécialisé avant d'être amorti.
Les
principes d'évaluation partielle décrits plus haut permettent
le développement rigoureux d'analyses de programmes
performantes [AC94,Con93a] et la conception de transformations de
programmes [CD90,CD91,Con93b] [2,12] pour des langages fonctionnels et
impératifs. Nos études conduisent à l'élaboration d'outils de
spécialisation. Ces outils ont un rôle crucial dans la
validation de la technologie que nous développons. Nos
efforts actuels portent sur un système d'évaluation partielle
pour le langage C, Tempo (voir module 5.1). Par ailleurs,
nous avons entrepris le développement d'un frontal à Tempo,
permettant la spécialisation de programmes Java (voir
modules 5.2, 5.3 et 6.1).
Sous-sections