• Aucun résultat trouvé

Evaluation de la machine d’injection et d’interception

Chapitre 10 Validation

1. Evaluation de la machine d’injection et d’interception

L’implémentation d’iPOJO est basée sur une machine d’injection et d’interception. Afin de quantifier le coût de cette infrastructure, il nous a paru intéressant de comparer les capacités d’injection d’iPOJO par rapport à d’autres technologies d’injection, et plus particulièrement le coût de l’injection d’iPOJO par rapport à d’autres technologies. Cette section décrit un banc d’essai mis au point pour l’évaluation et la comparaison des surcoûts engendrés par l’injection.

1.1. Description du banc d’essai

Ce banc d’essai est en fait une extension d’un banc d’essai existant (Facbench) qui s’intéressait au même problème. Les extensions faites concernent principalement les types de données échangées afin d’effectuer un banc d’essai testant de nombreux mécanismes internes.

L’idée globale du banc d’essai est simple. Deux composants (un serveur et un client) sont interconnectés (Figure 118). Le client a accès au serveur en utilisant la technologie testée. Lorsque le banc d’essai démarre, le client interagit un grand nombre de fois avec le serveur. Ces interactions utilisent différents types d’arguments et de retour, mais le serveur n’effectue aucune action réelle.

Figure 118. Acteurs du banc d'essai

1.2. Protocole de test

Le déroulement du banc d’essai est identique pour chaque technologie testée (Figure 119). Tout d’abord, les composants serveur et client sont créés. Ensuite, le serveur est mis à dispositif pour le client. Ceci s’effectue soit par injection dans le code du client (méthode set, passage dans le constructeur), soit par découverte lorsque celle-ci est supportée.

Une fois les composants initialisés, le banc d’essai effectue un « échauffement » afin d’éviter tous ralentissements dus à l’initialisation de la machine virtuelle (chargement de classe…). Lorsque ce préchauffage est effectué, le test est réellement effectué cinq fois. Une mesure est prise à chaque exécution.

Un test s’effectue en demandant au client d’appeler une méthode sur le serveur un grand nombre de fois (1 million). Quatorze méthodes sont appelées ainsi en variant les paramètres échangés (sans arguments, objet de petite taille, tableau, tableau de grande taille, collection de grande taille…). Les types de retour varient également.

La prise de mesure s’effectue avant et après la demande à client d’effectuer les appels au serveur (méthode doBench). Seront retenus pour l’analyse les temps minimaux sur l’ensemble des cinq tests. Plus ce temps est petit, plus la technologie d’injection est performante.

167 Figure 119. Fonctionnement du banc d'essai

Le banc d’essai est exécuté sur un ordinateur disposant d’un processeur 2,6 GHz Dual Core de marque Intel, fonctionnant sur Mac OS 10.5.4 (Leopard). La machine dispose également de 4 Go de mémoire à 667 MHz. Le banc d’essai est exécuté en utilisant Java 6.

1.3. Ordre de grandeur

Afin de comparer et d’évaluer le coût des technologies d’injection et d’interception, le banc d’essai a été également effectué sans infrastructure d’injection et d’interception sous-jacente, et avec des technologies de base tels que les proxies… Les mesures prises lors de ces tests nous permettent de positionner les différentes technologies d’injection et d’interception par rapport à des cas connus.

Tout d’abord, le banc d’essai est effectué en utilisant simplement Java avec des appels directs. Le client communique directement avec le serveur. Ensuite, le test est effectué avec des appels utilisant un protocole de synchronisation. En effet, la synchronisation (prise de verrou) est relativement couteuse en Java. Dans ce test, chaque méthode du serveur est synchronisée. Trois configurations testent différentes sortes de proxies. Dans ces cas le client n’accède pas directement au serveur, mais les appels sont relayés par un objet intermédiaire. Trois types de proxies ont été utilisés :

 Un proxy statique développé manuellement déléguant simplement les méthodes sur le serveur  Un proxy statique développé manuellement, mais utilisant la réflexion afin d’invoquer les

méthodes sur le serveur

 Un proxy dynamique généré par la machine virtuelle Java, dont l’intercepteur (InvocationHandler) délègue les appels sur le serveur.

Enfin a également été mesuré le coût des appels utilisant JMX (le serveur est exposé sous la forme d’un

MBean statique que le client invoque en utilisant JMX et un connecteur RMI). En effet, JMX est une technologie régulièrement utilisée pour la reconfiguration dynamique, car elle propose une infrastructure distribuée, et sécurisée permettant la supervision d’applications Java.

Clement Escoffier 168 Le tableau ci-dessous donne les mesures effectuées dans tous les cas cités auparavant. Nous remarquerons plus particulièrement le coût de la synchronisation par rapport à des appels directs et le coût des proxies dynamiques.

Type d’invocation Temps mesurés

Invocation directe

Invocation de méthodes synchronisées Serveur derrière un proxy statique

Serveur derrière un proxy statique avec appels réflectifs

Serveur derrière un proxy dynamique Serveur atteint avec JMX

Tableau 31. Ordre de grandeur pour le banc d'essai

1.4. Plates-formes et technologies testées

Afin de comparer iPOJO par rapport aux autres technologies d’injection et d’interception, plusieurs technologies ont été testées. Une implantation utilisant OSGi™ a été tout d’abord effectuée, suivit d’une implantation utilisant l’implémentation de Declarative Services disponible sur Apache Felix. Ensuite Spring Dynamic Modules a été évalué de deux manières différentes. Apache Tuscany, une implémentation libre de SCA a également été étudiée. Cette section décrit les configurations testées pour chacune de ces technologies ainsi que pour iPOJO.

1.4.1.iPOJO

Quatre implantations différentes du protocole de tests utilisant iPOJO seront décrites et analysées dans ce chapitre. Toutes testent l’injection d’objet de service et l’accès au service. La première implantation utilise l’injection dans des membres de la classe d’implémentation du client afin d’accéder au serveur (Figure 120).

Figure 120. Accès au serveur grâce à un membre injecté

La deuxième implantation optimise la première en diminuant le nombre d’accès au Thread Local (garantissant l’état du composant). En effet, et comme l’illustreront les résultats, la création et l’accès au Thread Local sont coûteux (Figure 121). Ce style de développement reste néanmoins protégé face au dynamisme. En effet, le serveur utilisé sera mis en cache jusqu’à la sortie de la méthode.

169 Figure 121. Utilisation d'une variable locale afin d'optimiser les temps d'injection

La troisième configuration effectue l’injection des objets de service (serveur) en utilisant des méthodes de liaisons. Il faut noter que ce style de modèle de développement demande nécessairement des blocs de synchronisation autour de l’accès au serveur. En effet, les méthodes de liaison injectant et retirant le serveur sont appelées dans un fil d’exécution différent de celui exécutant les tests (Figure 122).

Figure 122. Utilisation de méthode de liaison pour accéder au serveur

Enfin, la dernière implantation utilise des compositions. Le client est exécuté dans une composition qui importe le serveur (grâce à une importation de service). Ce teste vise à montrer le temps d’accès à un service

Clement Escoffier 170 importé au sein d’une composition par rapport à l’accès direct à ce service. Ce test utilise la première implémentation du client (injection dans des membres de la classe non optimisée).

1.4.2.OSGi et Declarative Services

Il nous a paru intéressant de quantifier le temps d’accès à un service en utilisant OSGi™ seulement. En effet, les services OSGi™ étant dynamiques, il est nécessaire de mettre en place un protocole de synchronisation complexe. Ce protocole a un coût non négligeable. Le code ci-dessous montre un aperçu du protocole de synchronisation nécessaire.

171 Une deuxième implantation utilisant Declarative Service a été réalisée. Le modèle de développement utilisé est sensiblement équivalent à l’implantation utilisant iPOJO et des méthodes de liaison. Nous avons utilisé l’implémentation de Declarative Services disponible sur Apache Felix.

1.4.3.Spring Dynamic Modules

Le banc d’essai décrit ci-dessus a été implanté de deux manières différentes avec Spring dynamic modules (version 1.1.1). La première implantation s’exécute au sein d’un seul bundle (Figure 124). En effet, Spring Dynamic Modules associe le contexte d’application (Application Context) est associé à un bundle OSGi™. Ainsi une application Spring sera conditionnée à l’intérieur d’un seul bundle. L’exécution intra-bundle ne relève pas des mêmes mécanismes qu’une implantation utilisant plusieurs bundles (qui aura alors différents contextes d’application).

Figure 124. Banc d'essai utilisant l'injection intra-bundle de Spring-DM

La deuxième implantation utilise justement deux bundles (Figure 125). Un bundle contient le serveur et exporte le service associé. Le second bundle contient le client et importe le service donnant accès au serveur.

Figure 125. Banc d'essai utilisant l'injection inter-bundle de Spring-DM 1.4.4.Apache Tuscany

Apache Tuscany est une implémentation libre de SCA. Bien qu’il ne supporte pas le dynamisme et n’est pas lié à une technologie d’implémentation, SCA est devenu une technologie incontournable. Nous avons choisi d’implanter les tests en Java. Deux tests ont été réalisés.

Le premier test s’exécute au sein d’un seul composite (Figure 126). Ceci permet à la plate-forme SCA d’injecter directement le serveur dans le client (grâce à une méthode de liaison).

Clement Escoffier 172 Figure 126. Banc d'essai dans un seul composite SCA

Le second test utilise deux composites distincts pour l’exécution du client et du serveur (Figure 127). Cependant, afin de donner accès au serveur, un troisième composite décrit les liens entre les deux composites sous-jacents. L’accès au serveur est alors décrit dans la description du composite global.

Figure 127. Banc d'essai utilisant plusieurs composites SCA

1.5. Résultats et analyses

Les résultats de ce banc d’essai sont visibles dans le tableau et le graphique ci-dessous. En vert sont représentées les valeurs des tests d’étalonnage, en bleu les résultats des différentes exécutions utilisant iPOJO et en rouge les autres technologies testées.

Tableau 32. Résultat du banc d'essai

Technology Metric Direct Invocation 132 iPOJO Optimized Service field injection 152

Static Proxy 235

Synchronized Invocation 633 OSGi Dynamic Service access 659 iPOJO Service Method Injection 670 Declarative Services 690 Spring Intra-bundle injection 831 iPOJO Service field injection 1389 iPOJO Imported service access 1397 Static Proxy with dynamic invocations 1723

Dynamic Proxy 1889

Invocation throught JMX 9380 Spring inter-bundle injection 12050 Tuscany intra-composite injection 12643 Tuscany inter-composite injection 12806

173 Figure 128. Comparaison des différentes technologies sur le banc d'essai

De ces résultats nous pouvons remarquer plusieurs détails intéressants. Tout d’abord, la première implémentation « naïve » utilisant iPOJO (injection de l’objet de service dans un membre de la classe) obtient de meilleures performances qu’un proxy dynamique. Ce résultat prouve l’intérêt d’une manipulation de bytecode par rapport aux mécanismes de réflexion. L’injection optimisée des objets de service obtient d’excellentes performances, car évite un grand nombre d’indirections. Ceci est possible, car l’objet utilisé reste capturé le temps de l’exécution de la méthode et des méthodes appelées (grâce au confinement par fil d’exécution). Les mécanismes de cache sous-jacents évitant les blocs de synchronisation sont plus rapides que les blocs synchronisés. Nous pouvons également remarquer que l’injection par méthode de liaison a des performances équivalentes par rapport à OSGi™ « pur » et à Declarative Services. Enfin, le temps d’accès d’un service importé à l’intérieur d’un composite est pratiquement équivalent à l’accès à ce même service en dehors d’un composite. Ce fait est dû à la republication du même objet de service dans le composite et non pas à un proxy.

Finalement, nous remarquerons que l’utilisation de technologies lourdes pénalise grandement les infrastructures telles que Tuscany ou Spring. Concernant ce dernier, Il faut noter que l’injection intra-bundle

(c'est-à-dire à l’intérieur du même contexte d’application) n’est pas dynamique et obtient donc de bonnes performances.

Clement Escoffier 174