• Aucun résultat trouvé

8.5 Exemple 2 : serveur web

8.5.3 Adaptation dynamique du nombre de threads

Puisque Comanche ne supporte que les requˆetes statiques correspondant `a des fichiers, ses perfor- mances sont limit´ees essentiellement par les entr´ees-sorties, qui bloquent le traitement d’une requˆete le temps de lire les donn´ees sur le disque. Pour pallier ce probl`eme, Comanche d´el`egue l’ordonnancement des requˆetes `a un composant scheduler, qui peut avoir plusieurs impl´ementations : traitement s´equentiel, un thread par requˆete, ou utilisation d’un pool de threads. Chaque impl´ementation a des avantages et inconv´enients, mais l’utilisation d’un pool de threads d´ej`a cr´e´es est le meilleur compromis entre temps de r´eponse et ressources utilis´ees. Cependant, les performances d’un tel composant d’ordonnancement d´ependent de la taille pour le pool, c’est-`a-dire du nombre de threads utilisables.

Notre dernier sc´enario d’adaptation va consister `a modifier dynamiquement le nombre de threads allou´es `a l’ordonnanceur. Plus le nombre de requˆetes qui peuvent effectivement ˆetre trait´ees en parall`ele est important, plus il est int´eressant d’allouer de threads pour pouvoir tirer partie de ce parall´elisme. En revanche, allouer trop de threads par rapport `a ce qui peut effectivement ˆetre utilis´e par le syst`eme nuit aux performances, car les threads eux-mˆemes sont gourmands en ressources (m´emoire et changements

9

En pratique, un tel ´ev´enement n’est pas g´en´er´e `a chaque fois que la m´emoire libre change, mais `a chaque fois qu’un tel changement est d´etect´e par WildCAT. La granularit´e temporelle des mesures (et donc les performances) d´ependent donc de la configuration de la sonde WildCAT correspondante.

10

de contexte). Le degr´e de parall´elisme d´epend quant `a lui : (i) du nombre total de processeurs pr´esents sur la machine, et (ii) de la proportion des requˆetes qui n´ecessitent un acc`es au disque, obligatoirement s´equentiel.

Informations contextuelles. Les informations dont nous avons besoin pour ´ecrire la politique d’adap- tation sont les suivantes :

– Le nombre de processeurs pr´esents sur la machine hˆote, qui d´etermine le nombre maximum de threads qui peuvent r´eellement s’ex´ecuter en parall`ele (en supposant qu’ils ne soient pas bloqu´es par des entr´ees / sorties). Ce nombre inclut `a la fois les processeurs physiques et les processeurs virtuels pr´esents sur les derni`eres g´en´erations de puces (Hyper-Threading). Le nombre de processeurs physiques peut ˆetre obtenu de WildCAT grˆace `a l’expression count(sys ://cpus/*), et pour chacun de ces processeurs physiques, l’attribut @flag_ht (pour Hyper-Threading) indique que la puce contient en fait deux processeurs. Le langage des expressions WildCAT n’est actuellement pas assez puissant pour permettre d’´ecrire une expression telle que sum(sys ://cpus/*@virtual_cpus), o`u virtual_cpusserait un attribut synth´etique valant 1 ou 2 suivant la valeur du drapeau @flag_ht. Actuellement, toutes les machines qui supporte l’Hyper-Threading sont homog`enes, c’est-`a-dire que si elles ont plusieurs processeurs physiques, ces derniers sont tous du mˆeme type. Nous utiliserons cette limitation pour d´efinir un attribut synth´etique sys ://cpus@virtual_cpus de la mani`ere suivante :

sys://cpus@virtual_cpus = count(sys://cpus/*) * if(sys://cpus/cpu0@flag_ht, 2, 1) Ce nouvel attribut indique le nombre total de processeurs disponibles sur la machine hˆote.

– Le taux de r´eussite du composant cache, c’est-`a-dire le pourcentage de requˆetes qui sont trait´ees directement par le cache sans acc´eder au disque. Plus ce taux est ´elev´e, plus le nombre de requˆetes qu’il est possible de traiter en parall`ele est grand. Cette valeur est accessible sous la forme d’un param`etre en lecteur seule (hitRate) du composant cache d´ecrit dans le sc´enario pr´ec´edent. Politique d’adaptation. Ce sc´enario d’adaptation n´ecessite deux politiques d’adaptation, car il fait interagir des composants « ´eloign´es » dans l’architecture de l’application (le cache et l’ordonnanceur). La politique principale, destin´ee au composite frontend qui encapsule l’ordonnanceur, est tr`es simple :

policy adaptive-scheduling = { rule{

when a:parameter-changed($target/child::*[contains(name(.), ’cache’)]/@hitRate) do { procs := sys://cpus@virtual_cpus; rate := $a.new-value; set-value($target/scheduler/@poolSize, (2 + 4*$rate)*$procs; } } }

Son unique r`egle d´etecte les ´ev´enements endog`enes correspondant aux variations du taux de r´eussite du cache11. Lorsque ce taux change, l’action de reconfiguration consiste simplement `a ajuster le nombre

de threads allou´es `a l’ordonnanceur en tenant compte `a la fois du nombre de processeurs disponibles et du taux de r´eussite du cache. Chaque processeur se voit allouer initialement deux threads, et ce nombre est augment´e proportionnellement au taux de r´eussite du cache (exprim´e en pourcentages). Ainsi, une machine quadri-processeur dont le cache fonctionne r´epond directement `a 50% des requˆetes se verra allouer (2 + 4 ∗ 0.5) ∗ 4 = 16 threads.

Cette premi`ere politique acc`ede au composant cache en utilisant le chemin FPath $target/child: :*[contains(name(.), ’cache’)]/@hitRate. Or, dans l’architecture initiale, le composant cache fait partie du composite handler et non du composant cible de cette politique (frontend, aka $target),

11

Si la machine hˆote supporte l’ajout et le retrait dynamique de processeurs, il suffit de changer le descripteur d’´ev´enement pour prendre en compte ce nouveau param`etre : parameter-changed(...) or changed(sys ://cpus@virtual_cpus).

Fig. 8.6 – Architecture globale de Comanche.

comme le montre la figure 8.6. Cependant, puisque Fractal supporte le partage de composants, rien n’empˆeche le cache de faire aussi partie de frontend. C’est ce que permet d’obtenir la seconde politique de notre sc´enario : policy adaptive-scheduling-helper = { rule { when b:binding-created($target/dispatcher/interface::*) if ($b.server-interface/component::*[contains(name(.), ’cache’)]) do { new-cache := $b.server-interface/component::*; frontend := $target/ancestor::comanche/frontend; old-cache := $frontend/cache;

if (old-cache != new-cache) then { remove($frontend, $old-cache); add($frontend, $new-cache); } } } }

Celle-ci est destin´ee au composant handler, tout comme la politique de gestion du cache du premier sc´enario. Elle r´eagit `a l’activation du cache en d´etectant la cr´eation d’une connexion entre le composant dispatcheret un composant dont le nom contient cache. De cette mani`ere, `a chaque fois que la politique d’adaptation du sc´enario pr´ec´edent r´e-active le cache (action enable-cache), cette r`egle est d´eclench´ee. Sa r´eaction consiste `a ajouter le nouveau composant cache au composite frontend (apr`es avoir ´eventuel- lement supprim´e l’ancien). Le cache actif est ainsi toujours disponible localement dans frontend, ce qui permet le bon fonctionnement de la premi`ere politique.

Cette manipulation est rendue obligatoire par l’impl´ementation actuelle de safran, car pour des raisons de performances la d´etection des ´ev´enements endog`enes (dont bindingcreated et parameter- changed) est limit´ee en terme de port´ee au composant source de l’´ev´enement et `a ses parents directs. Si cette limitation n’existait pas, la premi`ere politique, adaptive-scheduling, pourrait r´ef´erencer di- rectement le composant cache par $target/parent::*/backend/handler/cache/@hitRate dans son descripteur d’´ev´enement12.

12

Cette solution aurait cependant le d´esavantage de rendre la politique directement d´ependante de la localisation du cache, information qui est actuellement encapsul´ee dans la politique adaptive-scheduling-helper.

D´eploiement. Le d´eploiement de la politique adaptive-scheduling peut se faire `a n’importe quel moment, mais ce n’est pas le cas de adaptive-scheduling-helper. En effet, cette derni`ere r´eagit `a un ´ev´enement endog`ene (l’activation du cache) qui est d´eclench´e par une autre politique attach´ee au mˆeme composant. ´Etant donn´ees les r`egles de composition qui s’appliquent lorsque plusieurs politiques sont attach´ees `a un mˆeme composant (cf. Section 7.4.4), adaptive-scheduling-helper doit donc ˆetre attach´ee apr`es la politique adaptive-cache du premier sc´enario. Une fois toutes les politiques d´eploy´ees, le nombre de threads allou´es `a l’ordonnanceur sera ajust´e automatiquement au nombre de processeurs pr´esents et au taux de r´eussite du cache, `a chaque fois que ce taux change ou qu’un nouveau cache est activ´e.