• Aucun résultat trouvé

Fragmentation syntaxique

9.2 Critères de fragmentation et abstractions de fragmentations

9.2.2 Fragmentation syntaxique

Le critère précédent considérait des programmes dans lesquels les index utilisés dans les accès aux tableaux sont des expressions simples de la forme A[i]. Pour des accès plus généraux, ne serait-ce que des accès de la formeA[i+1], il faut préciser le critère. Cela vaut aussi bien pour les accès aux tableaux que pour les accès aux structures de données chaînées. Si, par exemple, une instruction modifie la valeur d’un nœud pointé par une variablepalors les prédicats de base sont satisfaisants. Mais si au contraire, le nœud modifié est le nœud suivant le nœud pointé, c’est celui là qui doit être distingué et non le nœud pointé.

Modifier les critères pour prendre en compte ces particularités est un point de détail. Ce-pendant la construction d’un critère de fragmentation consiste essentiellement à être capable de prendre en compte toute un ensemble de détails de ce genre. Les critères devenant de plus en plus complexes, il devient difficile de continuer à les généraliser sans briser leurs qualités acquises.

Les auteurs de [HP08] dont nous avons déjà discuté en section 9.1.4 proposent un critère de fragmentation plus précis tenant compte du point dont nous venons de parler, mais également d’autres caractéristiques du programme moins évidentes.

Les auteurs proposent de fixer la syntaxe des programmes à analyser. Cette syntaxe peut pa-raître contraignante, mais il est tout à fait possible de l’étendre sans mettre en défaut l’analyse. Simplement, elle définit une classe de programmes sur lesquels on a bonne confiance que le critère sera pertinent. C’est d’ailleurs un très bon moyen de décrire les limites d’un critère de fragmentation : on sait où se situe le travail à accomplir lorsque l’on étend la syntaxe.

Nous détaillons brièvement les caractéristiques de ce langage. Les restrictions ne s’appliquent pas aux expressions, qui peuvent être quelconques. D’abord, on sépare les variables du pro-grammes en deux groupes : les variables utilisées pour indexer les tableau, et les variables de contenu, de même type que les cellules de tableau. Ainsi, on sait exactement quelles sont les variables susceptibles d’être utilisées pour partitionner les tableaux. Ensuite, la principale res-triction s’applique aux structures de contrôle. On peut employer librement des structures condi-tionnelles, mais la seule structure répétitive que l’on s’autorise est une boucle simple dont la syntaxe est la suivante :

for ( i := <Iexp>; <cond>; <progress>)

<statement>

où<Iexp>est une expression pouvant contenir des variables d’indice,<cond>est n’importe quelle condition,<progress>est au choix++ou-et<statement>peut être n’importe quelle instruction du langage. La sémantique est la suivante. On initialisera la variable d’indiceiavec

<Iexp>avant d’entrer dans la boucle, puis qu’on exécutera<statement>tant que la condition

<cond>est vraie, tout en incrémentant ou décrémentant la variableide 1 entre deux exécutions selon que<progress>est respectivement++ou-.

Le langage n’autorise pas de sauts inconditionnels ou de sortie prématurées d’une boucle. Ceci donne une forme bien particulière au graphe de flot de contrôle : les seules composantes fortement connexes sont les boucles et elles n’ont qu’une seule entrée et qu’une seule sortie.

Le critère de fragmentation proposé par les auteurs se base sur cette bonne structuration du programme. Pour chaque boucle, on utilisera un partitionnement différent de l’ensemble des valeurs d’indices, qu’on établira en suivant trois règles :

• Le partitionnement dans une boucle doit être un raffinement du partitionnement au niveau supérieur. Pour une boucle imbriquée, ceci signifie qu’on doit raffiner la partition de la

boucle englobante. Une partition est un raffinement d’une autre si les parties de la seconde peuvent être obtenues par union des parties de la première.

• Dans une boucle toujours, la partition doit distinguer les cellules selon que leur indice est inférieur ou supérieur à l’expression d’initialisation< Iexp >. Pour les boucles où la progression est++, on distinguera les cellules d’indice` < Iexpet` ≥ Iexptandis que si la progression est-on distinguera les cellules d’indice`≤Iexpet` >Iexp.

• Chaque fois qu’une expression A[Iexp] apparaît, la partition à ce niveau d’imbrication devra distinguer les cas` <Iexp,`=Iexpet` >Iexp.

Ce partitionnement de l’ensemble d’indices est utilisé pour produire un partitionnement de chaque tableau. Au cours de l’analyse, on ajoutera également de nouvelles tranches de tableau, construites comme translations non symboliques de ces tranches originales. Ces translations sont ajoutées dans deux cas. D’une part lorsque l’expression d’indice d’une cellule à droite d’une affectation est la même à une addition de constante près que l’expression d’indice d’une cellule à gauche de l’affectation. D’autre part, dans une phase de normalisation quand il est possible d’associer de l’information à une telle translation.

Ce critère permet effectivement de distinguer dans les boucles la cellule en cours de manipu-lation, les cellules déjà manipulées et les cellules qui ne l’ont pas été. Par rapport aux travaux présentés dans la section précédente, il a plusieurs avantages. D’abord il permet de désigner cor-rectement les fragments singletons en considérant l’expression d’indexation réellement utilisée dans chaque accès. Ensuite, il reste pertinent lorsqu’une boucle ne commence pas aux extrémités d’un tableau. Si une boucle croissante commence à l’indice 2 au lieu de 1, les propriétés que cette boucle construit ne sont valables qu’à partir de la deuxième cellule du tableau. La seconde règle du critère permettra de séparer la première cellule du reste. Pour la même raison, cette seconde règle sera aussi utile dans certains algorithmes utilisant des boucles imbriquées : si la boucle imbriquée ne démarre pas au même indice à chaque itération, on sera en mesure de distinguer les cellules parcourues lors de l’itération courante de la boucle externe et celles parcourues lors de des itérations précédentes.

Enfin, le critère permet, comme dans la section précédente, de distinguer les cas d’alias. Mais cette fois, les tranches vides étant autorisées, le nombre de configurations est bien moindre. Si on doit partitionner un tableau selon deux variables d’indicesiet j incomparables, seules 32 tranches devront être définies. Suivant les positions relatives deiet j, on n’a qu’à distinguer trois configurations :

<i, < j i >i, < j j >i, > j <i, < j j > j, <i i >i, > j

<i, < j i= j >i, > j

Toutes les tranches de ces configurations à l’exception de deux ne sont présentes que dans une seule des configurations. Donner des propriétés à ces tranches revient à ne donner des propriétés à une partie du tableau que dans l’une des trois configurations. On peut en particulier traiter précisément les programmes dont le comportement est différent selon quei = joui , j. En revanche, les tranchesA[< i, < j] etA[> i, > j] qui sont présentes dans les trois configurations ne peuvent être traitées différemment selon les cas.

Le critère peut introduire au pire cas un nombre de fragments exponentiel en le nombre d’ex-pressions d’indice. Ce pire cas est rarement atteint et est une réduction importante de la com-plexité par rapport à la double exponentielle de [LARSW00] qu’on retrouverait en distinguant les configurations selon que les tranches sont vides ou non.

Le critère sémantique de fragmentation utilisé dans cette thèse s’inspire grandement de ce critère syntaxique. Il tente d’en reproduire les effets, notamment dans sa version développée. (Cf. section 4.1.4) Nous avons effectivement cherché à reproduire l’action de la seconde règle, en séparant dans les boucles imbriquées les cellules accédées lors de l’itération courante de la boucle externe et celles accédées lors de ses itérations précédentes.

Nous donnons maintenant quelques différences de notre critère par rapport au critère syn-taxique de [HP08]. Notre critère maintient une partition distincte pour chaque tableau et consi-dère de la même manière les cellules lues et les cellules écrites. Cela n’entrainera généralement aucune différence par rapport au critère syntaxique qui choisira astucieusement les bonnes trans-lations à considérer dans chaque tableau. Il s’agit là d’une différence assez subtile entre les deux méthodes, qui produira rarement des différences dans les fragmentations désignées.

Ici, nous nous intéresserons plutôt aux différences inhérentes à la nature syntaxique du critère. En changeant la syntaxe d’un programme sans en changer la sémantique, on peut mettre en difficulté le critère. En particulier, si deux indices sont égaux et qu’on utilise l’un d’entre eux pour accéder à un tableau, il n’est pas toujours évident que c’est celui-là qu’il faut inclure dans la partition. Considérons le programme suivant.

A[1] ← 0

i ← 2

Tant quei<nfaire

A[i] ← 1

i ← i+1

Le critère syntaxique produira les tranchesA[1],A[2..i[,A[i],A]i..n] avec lesquelles l’analyse du programme sera précise. On peut modifier le programme sans changer le résultat de son exécution :

i ← 1

A[i] ← 0

i ← i+1

Tant quei<nfaire

A[i] ← 1

i ← i+1

Cette fois, le critère syntaxique ne distinguera plusA[1] qui sera confondue dans la tranche

A[1..i[. Le programme ne peut plus être analysé précisément. Notre critère à l’inverse distin-guera les cellules selon l’instruction à laquelle elles ont été accédées et produira les mêmes fragmentations pour les deux programmes ci-dessus.

Un autre exemple de programme qui ne peut directement être analysé par le critère syntaxique est le programme qui cherche non seulement un élément maximum dans un tableau, mais égale-ment son indice :

max ← A[1] imax ← 1 Pouri de2à nfaire SiA[i]>maxalors max ← A[i] imax ← i

Puisque imaxn’apparait dans aucun des accès aux tableaux, il ne sera pas présent dans la partition. Tandis que le critère sémantique utilisera l’égalité entreietimaxcomme prétexte pour conserverA[imax]. Notons que si on réécrit le test enA[i]> A[imax] alors le critère syntaxique pourra être utilisé.

Nous donnons un dernier exemple pour lequel le critère sémantique peut potentiellement don-ner un meilleur résultat mais ne le fera pas si la fragmentation est abstraite par des diagrammes de tranches. Pouri de1à nfaire Sii≤ jalors A[i] ← 1 sinon A[i] ← 0

Encore une fois, le critère syntaxique ne créera qu’une seule tranche pour toutes les cellules affectées, tandis que le critère sémantique cherchera à séparer les effets des deux affectations. Cependant, les diagrammes interdisent l’utilisation de la trancheA[j..i] car il n’est pas vrai à tout moment de la boucle que j≤i.

En pratique, ce critère donnera très souvent des bonnes partitions de tableau mais son caractère syntaxique lui fait perdre en souplesse et une légère modification de la syntaxe peut le mettre en défaut. On notera toutefois que les critères sémantiques, et pas seulement celui introduit dans cette thèse, peuvent également être mis en défaut par des manipulations de ce genre : des égalités d’indices inopportunes peuvent compliquer significativement le choix des bons indices à utiliser là où le premier choix, celui de l’expression syntaxique, convient. Un tel exemple est donné en section 10.2.3.