• Aucun résultat trouvé

La QoS en Junior

3.4 Pistes

3.4.2 La QoS en Junior

)

Les d´esavantages de cette solution sont doubles :

(a) Elle n´ecessite un g´en´erateur d’´ev´enements uniques qui n’existe pas dans tous les langages. (b) La port´ee d’un ´ev´enement n’apparait pas syntaxiquement et est difficile `a d´eterminer.

Les instructions ´ev´enementielles param´etrables permettraient de r´esoudre en partie ce probl`eme de la fa¸con suivante :

Environment E = new SubEnvironment ( ) ;

J r . Par ( J r . Loop ( J r . Seq ( J r . G e n e r a t e (E , ” s t e p ” ) ,

J r . Repeat ( 3 , J r . Stop ( ) ) ) ) ,

J r . C o n t r o l (E , J r e . P r e s e n c e (E , ” s t e p ” ) , b e h a v i o r ( ) )

)

L’´ev´enement"step"n’est vu que par l’instruction Control ce qui implique que : 1) son corpsbehavior()

peut recevoir ou ´emettre l’´ev´enement"step", et 2) l’instruction Local n’est plus n´ecessaire. La port´ee de l’´ev´enement "step"est identifi´ee par la variableE.

Pour utiliser les sous-ensembles, il faudrait cr´eer un nouveau type de configuration Presence (Jr.Presence( environnement, identifier)) pour chercher les ´ev´enements dans un sous-ensemble donn´e. L’impl´ementation de la configuration Presence ne serait cependant pas suffisante et il faudrait aussi modifier le moteur r´eactif pour d´etecter la g´en´eration d’un ´ev´enement dans un sous-ensemble et r´eagir `a cet ´ev´enement.

Les instructions ´ev´enementielles param´etrables sont `a l’´etude et on n’a pas encore une impl´ementation et une formalisation de leur description. Cet ´etude fait partie des futures recherches `a r´ealiser.

Ces deux propositions permettent de d´ecrire des comportements r´eactifs que l’on ne peut pas programmer dans la version originale de Junior. Ces deux m´ecanismes sont compl´ementaires car le pattern matching ne peut pas ˆ

etre impl´ement´e avec les instructions ´ev´enementielles param´etrables et vice-versa. Le pattern matching permet d’attendre plusieurs ´ev´enements en mˆeme temps tandis que les instructions ´ev´enementielles param´etrables ne testent qu’un ´ev´enement `a la fois. Inversement, avec les instructions ´ev´enementielles param´etrables on peut s’assurer que les ´ev´enements g´en´er´es dans un sous-ensemble ne seront pas ´ecout´es pas d’autres comportements alors qu’avec le pattern matching tout le monde peut se mettre `a ´ecouter n’importe quel ´ev´enement.

3.4.2 La QoS en Junior

Dans cette section on va d´ecrire quelques exp´erimentations faites en Rewrite et Replace pour essayer de faire de la QoS (Quality of Service, Qualit´e de Service) en Junior. L’id´ee est de pouvoir garantir un certain nombre de propri´et´es, par exemple, que le syst`eme est capable de r´eagir `a un ´ev´enement en un certain temps. La technique g´en´erale (qui est largement utilis´ee dans les protocoles de communication des ordinateurs) consiste `a limiter les ressources utilis´ees. Par exemple, on pourrait limiter le temps d’ex´ecution de toute action atomique ou limiter le nombre d’instructions `a ex´ecuter.

Une premi`ere exp´erimentation faite par F. Boussinot [BOU 00b] a consist´e `a construire un automate qui impl´emente le programme r´eactif. Le probl`eme de cette technique est que les automates ont tendance `a exploser en taille d`es que l’on programme un comportement ”complexe”. Pour limiter l’impact de ce probl`eme, il est

propos´e la construction d’automates partiels. C’est-`a-dire que le programme n’est pas enti`erement construit `a la compilation et les parties manquantes sont compil´ees et ex´ecut´ees `a l’ex´ecution lorsqu’elles sont sollicit´ees. Mon id´ee consiste `a modifier Junior pour limiter le temps d’ex´ecution d’un atome et limiter le nombre d’instructions `

a ex´ecuter; l’id´ee est de ne pas construire l’automate ´equivalent du programme r´eactif et d’utiliser la structure d’arbre de tout programme Junior. Voici la description de ces exp´erimentations.

Limitation du nombre d’instructions `a ex´ecuter

Limiter le nombre d’instructions `a ex´ecuter a comme objectif de limiter le temps de r´eaction. Le temps de r´eaction d’un programme r´eactif d´epend : 1) du temps d’ex´ecution de chaque type d’instruction, 2) du nombre d’instructions ex´ecut´ees de chaque type et, 3) du temps d’ex´ecution du code Java ex´ecut´e dans les atomes et dans les wrappers. Pour simplifier les choses, on va consid´erer que le temps d’ex´ecution de chaque type d’instruction est le mˆeme et que le temps d’ex´ecution des atomes et wrappers est nul. Avec ces deux consid´erations, on va estimer le temps d’ex´ecution d’un programme comme la simple multiplication du nombre d’instructions par une constante.

Avec l’estimation du temps de r´eaction d’un programme et en connaissant le nombre de programmes ex´ecut´es dans la machine, on va pouvoir ”garantir” que tout le syst`eme r´eagit en moins d’un temps donn´e. Pour ´eviter que le temps de r´eaction ne d´epasse une certaine valeur, on fait deux choses : 1) on ne charge pas de nouveaux programmes r´eactifs, et 2) on ex´ecute les programmes r´eactifs normalement jusqu’au moment o`u ils d´epassent un certain nombre d’instructions, `a ce moment l`a on les pr´eempte.

Pour limiter le nombre d’instructions `a ex´ecuter, on a utilis´e la structure d’arbre qui existe dans certaines des impl´ementations de Junior. L’id´ee consiste `a modifier Junior pour avoir deux compteurs, un qui compte la hauteur de l’arbre et un autre qui compte le poids de l’arbre. Le coˆut du comptage est minime car de toute fa¸con on visite les instructions lorsqu’on les active. Le seul probl`eme avec cette impl´ementation est la fa¸con dont on compte les instructions; en Simple, par exemple, il n’y a pas d’arbre d’ex´ecution, et entre Rewrite et Replace on ex´ecute un nombre diff´erent d’instructions pour le mˆeme programme (Replace ne cr´ee pas des instructions Seq dans les boucles). Cette diff´erence entre Rewrite et Replace finit par ˆetre utile car, par exemple, elle montre comment Rewrite consomme plus de ressources que Replace. On n’a pas fait l’exp´erimentation en Simple ou Glouton mais l’impl´ementation des compteurs (malgr´e l’absence d’un arbre d’ex´ecution) ne pose aucun probl`eme.

L’avantage du m´ecanisme de comptage d’instructions `a la vol´ee, est que l’on ne perd pas du temps en construisant et en analysant l’automate du programme. D’autre part, il pr´esente l’inconv´enient de ne pas pouvoir prendre la d´ecision d’ex´ecuter un programme avant mˆeme de l’ex´ecuter. Il y a deux fa¸cons de proc´eder:

1. Ne pas ex´ecuter un programme r´eactif en fonction d’une estimation du nombre d’instructions `a ex´ecuter. Pour cela on peut utiliser le nombre d’instructions ex´ecut´ees pr´ec´edemment, le nombre maximal d’instructions que l’on pourrait ex´ecuter et tout cela modulo un facteur de marge.

2. Arrˆeter l’ex´ecution d’un programme r´eactif au cours de l’activation lorsqu’il d´epasse l’utilisation des ressources allou´ees, par exemple, la hauteur, le poids, etc.

La deuxi`eme option pr´esente le probl`eme g´en´eral de la pr´eemption forte: on peut laisser le syst`eme dans un ´

etat instable. Les exp´erimentations faites ont consist´e `a mettre au point le comptage des instructions ex´ecut´ees dans Rewrite et Replace. Ensuite, il suffit d’arrˆeter l’ex´ecution d’un programme en utilisant une instruction limitant les ressources `a utiliser; pour limiter les ressources on pourrait imaginer l’instruction suivante :

Jr.Limite(Characteristic, Value, Body, Handler)

o`uCharacteristicd´efinit le type de ressource `a limiter (par exemple la hauteur maximale de l’arbre d’ex´ecution),

Valued´efinit le nombre de ressource que l’on peut utiliser,Body est le programme r´eactif `a limiter etHandler

est le programme r´eactif ex´ecut´e lorsque le corps est interrompu car il a d´epass´e les limites.

J r . L i m i t e ( J r . Height , 2 ,

J r . Seq ( J r . P r i n t ( ”QoS” ) , J r . Loop ( J r . Stop ( ) ) , J r . P r i n t ( ”QoS Pr´eemption ” ) )

Le programme ex´ecute son corps jusqu’au point o`u il d´epasse la hauteur permise (2), c’est-`a-dire il ex´ecute les instructions Seq et Loop, et lorsqu’il va commencer l’ex´ecution de l’instruction Stop, le programme est pr´eempt´e et le handler est ex´ecut´e (on imprimeQoS Pr´eemption).

Les atomes born´es en temps

Une autre exp´erimentation qui a ´et´e faite est l’impl´ementation des actions atomiques born´ees en temps. L’id´ee est de pouvoir stopper les programmes qui, pour une raison quelconque, ne finissent pas ou prennent beaucoup de temps. Limiter le temps d’ex´ecution d’une action atomique a deux objectifs:

1. Garantir l’´evolution globale du syst`eme. Voici un programme r´eactif qui arrˆete l’ex´ecution de tout le syst`eme :

J r . Atom { f o r ( ; ; ) ; }

Dans cet exemple on a simplifi´e la syntaxe r´eelle des actions atomiques (celle utilis´ee dans la section2.3.5) pour faciliter la lecture. Dans la suite on utilisera la syntaxe simplifi´ee.

Limiter le temps d’ex´ecution de ces programmes, empˆecherait la propagation de ce genre d’erreurs aux autres comportements r´eactifs ex´ecut´es en parall`ele.

2. Garantir le fonctionnement du syst`eme dans des conditions acceptables, autrement dit, faire d’une autre fa¸con de la QoS. Par exemple, si on limite le temps d’ex´ecution d’un programme, on peut assurer que le syst`eme ne s’´ecroulera pas lorsqu’on ajoutera un nouveau comportement. Si c’est le cas, on peut refuser l’ajout.

Les exp´erimentations faites ont montr´e qu’un m´ecanisme efficace de threads pr´eemptifs est n´ecessaire car ce que l’on veut faire est de stopper le thread qui ex´ecute le programme r´eactif et puis reprendre l’ex´ecution dans une instruction r´eactive ”sˆure”. Tout le probl`eme consiste `a reprendre l’ex´ecution `a partir d’une instruction r´eactive d´esign´ee. Par exemple, pour le programme pr´ec´edent, il faudrait cr´eer deux threads, un thread principal qui ex´ecute les instructions r´eactives Loop et Seq, et un thread auxiliaire qui ex´ecute l’action atomique. Le thread principal se suspend lorsqu’il ex´ecute l’atome et, `a ce moment, il passe la main au thread auxiliaire qui ex´ecute le code Java. Si le thread auxiliaire d´epasse le temps allou´e, on le tue et on r´eveille le thread principal avec un code d’erreur. Si le thread auxiliaire finit normalement, on redonne la main au thread principal avec un code de succ`es.

Le probl`eme de cette impl´ementation en Java est la cr´eation d’un nouveau thread `a chaque fois que l’on ex´ecute un atome. La cr´eation ´etant assez lourde, il vaut mieux le suspendre lorsque l’atome finit normalement et le relancer `a la prochaine ex´ecution d’un atome. Malheureusement l’op´eration pour suspendre un thread Java a ´

et´e ´elimin´ee (dans le jargon de Java, on dit deprecated ) et il faut donc impl´ementer un m´ecanisme `a soi pour le suspendre de fa¸con propre. Mais ce n’est pas tout le probl`eme :

1. Il ne faut pas impl´ementer la suspension avec une attente active car sinon on peut consommer des ressources inutilement. On doit utiliser sleepou les primitiveswaitet notify.

2. Il faut g´erer correctement les diff´erents threads cr´e´es: le thread principal, le thread auxiliaire et le thread qui impl´emente le timer. En particulier il faut faire attention aux priorit´es.

En conclusion j’ai eu du mal `a mettre au point une version qui marche bien et qui passe `a l’´echelle.

Pour r´eduire la cr´eation de threads et avoir un contrˆole plus fin sur le temps d’ex´ecution d’un programme, on pourrait cr´eer une instruction qui pr´eempte un comportement r´eactif et pas seulement une action atomique.

Appelons cette instructionpreemption, que l’on utilise de la fa¸con suivante :

Jr.Preemption(Nombre d’instructions, Body, Handler)

Voici un exemple de cette instruction : J r . Preemption ( 3 0 ,

J r . Loop ( J . Seq ( J r . Atom { f o r ( ; ; ) ; } , J r . Stop ( ) ) ) ,

J r . Atom { System . o u t . p r i n t l n ( ” Timer e l a p s e d ” ) ; } )

Ce programme pr´eempte l’action atomique (constitu´ee par l’instructionfor) lorsqu’elle prend plus de 30 secon-des en s’ex´ecutant. Lorsque la pr´eemption est effective, le handler est ex´ecut´e. Le probl`eme de cette instruction est la s´emantique que l’on donne `a un programme qui ex´ecute une instruction ´ev´enementielle, par exemple l’instruction Await du programme suivant:

J r . Par ( J r . Preemption ( 3 0 ,

J r . Seq ( J r e . Await ( ”E” ) ,

J r . Atom { System . o u t . p r i n t l n ( ”E e s t p r ´e s e n t ” ) ; } , J r . Seq ( J r e . Repeat ( 2 5 , J r . Stop ( ) ) ,

J r e . G e n e r a t e ( ”E” ) ) )

L’instruction Wait peut prendre beaucoup de temps `a finir, mˆeme lorsque l’´ev´enement est g´en´er´e durant la premi`ere activation; par exemple, lorsqu’on utilise Replace et que l’on tombe sur un programme de type cascade inverse. Ensuite, il faut voir comment interpr´eter le temps que prend l’instruction lorsque l’´ev´enement n’est pas pr´esent durant instant; il faudrait ´egalement ne pas consid´erer le temps d’ex´ecution inter-instant.

Finalement, il faut dire que l’impl´ementation des actions atomiques born´ees en temps doit ˆetre accompagn´ee par une instruction qui permet de d´etecter lorsque l’atome d´epasse le temps. Les tests que l’on a faits ont montr´e que, par exemple, la cr´eation d’objets peut prendre beaucoup de temps et que, lorsque cela arrive, il faut souvent arrˆeter tout le comportement et g´erer le probl`eme au cas par cas. Autrement dit, il faut un m´ecanisme de contrˆole d’erreurs; la section suivante pr´esente une proposition pour r´esoudre ce probl`eme.