Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Prochaines sessions inter-entreprises : 28-31 mars 2017 / 13-16 juin 2017
Sessions intra-entreprises sur demande.
Inscrivez-vous vite !

Paris JUG "Performance" : compte-rendu et wave !

Le 18 janvier dernier se tenait un Paris JUG exceptionnel car animé par un Java Champion : Kirk Pepperdine. La thématique était le dépistage et la résolution des problèmes de performances sur des applications Java.

De l'avis général, si Kirk est un excellent showman qui sait captiver son public, le fond de sa présentation était relativement pauvre.
Personnellement, suis resté sur ma faim, car les quelques conseils donnés sonnaient comme des évidences :

  • Il faut mesurer et non "ressentir" les performances. De plus, pour que la mesure soit répétable, il faut mettre en place une plateforme de test standardisée (JMeter...).
  • Une mesure donne un fait objectif irréfutable, ce qui permet d'apaiser les tensions dans l'équipe et par rapport au client.
  • Il faut mesurer chaque élément du système (application, plateforme, réseau...), trouver le plus gros consommateur de performances et l'optimiser ; répéter cette opération jusqu'à obtention des performances souhaitées, normalement spécifiées par le client dès la phase de conception.
  • Enfin, il n'y a pas de solution universelle pour l'amélioration des performances. Même les conseils de spécialistes doivent être appliqués avec circonspection et soumis à la mesure.

Vous trouverez ci-dessous la retranscription de la séance sous la forme d'une Wave, saisie en temps réel depuis mon netbook (vous allez devoir retrouver votre compte Google Wave pour y accéder :).
Bonne lecture !

Note : pour les lecteurs ne possédant pas de compte Wave, la retranscription textuelle de la conférence est également disponible au bas de ce billet.

Performance avec Java

Dan simule un appel d'un uitilisateur qui rapporte qu'un site est lent.

Le premier réflexe d'un programmeur est de vérifier s'il obtient le même problème, souvent depuis un simple browser, ce qui ne signifie rien.

Les gens tentent de mesurer la performance de manière non efficace, par exemple depuis un brower : Performance anti-patterns. Les équipes se rejettent la responsabilité sans avoir réellement mesuré. Individuellement, le DBA, le responsable réseau, le développeur disent que tout est OK de leur côté.

Beaucoup de stress à tous les étages, y compris pour les managers qui doivent expliquer ça au client.
Le problème c'est que le stress a un fort impact négatif sur la capacité à résoudre les problèmes et à interagir socialement.

Le premier réflexe d'un programmeur est évidemment de se focaliser sur le code pour chercher les problèmes. Mais le code est le pire endroit où regarder : ce n'est pas la bonne granularité. Il faut regarder au niveau d'un composant ou d'une application, en situation réelle (pas sur le poste du développeur !)

Testing antipatterns

AntiPattern 1

Utiliser un brower pour tester une webapp : non répétable, non mesurable. Il faut utiliser un framework/environnement de test (ex: JMeter).

JMeter est gratuit, et si on le comprend bien on peut obtenir autant d'informations qu'avec des outils commerciaux, avec le bénéfice qu'on pourra l'utiliser dans toutes les situations et sur tous les projets.
Jmeter permet de simuler des requêtes HTTP de manière répétable

On constate que les threads du serveur d'app sont très occupés par du traitement côté serveur. D'où cela peut-il venir ? Peut-être de la base de données, ou du code sur le serveur. Il faut donc investiguer en prenant, par exemple, des thread dumps pour voir pourquoi ils sont occupés.

L'analyse du code montre un code horrible qui gère manuellement les sessions des utilisateurs et une synchronisation plus que hasardeuse. (Ils ont dû s'amuser pour écrire un code aussi affreux :p)

Essayons avec un code moins horrible, qui utilise une ConcurrentHashMap. Un rapide poll du public pour savoir si ça va être plus rapide ou moins rapide montre que personne n'ose se mouiller et que donc, la mesure et l'amélioration de la perfoamnce sont des sujes hasardeux et mal maîtrisés, même parmi les membres du PJUG.

Sur cet exemple particulier, il se trouve qu'améliorer les perfs du composant incriminé a ralenti l'ensemble des composants qui y font appel ! ("hurry up and wait")

AntiPattern 2

Shot in the dark

Un refactoring sur un bout de code a des effets (positifs ou négatifs) sur les performances, mais personne ne sait pourquoi. Pire, du code "moche" peut être plus performant que du "beau" code, il faut donc résister à la tentation de refactorer du code moche en pensant optimiser.

Il faut d'abord identifier ce qui consomme le plus de temps (dominating consumer") : hardware (cpu, mémoire, disk IO, network), JVM ou OS (mémoire, drivers...) ou application (locks, interaction avec les systèmes externes) ?

SI c'est la JVM qui prend le plus de temps, il faut peut-être tuner le nombre d'objets créés, ou le garbage collector. Toujours au niveau de la JVM, qu'es-ce qui retient les threads hors de l'état Runnable ?

Il faut effectuer un tuning mémoire, tuning réseau, tuning des locks.
Sous unix (linux/mac...) on peut utiliser vmstats. Cet outil génère beaucoup d'informations mais il faut savoir le lire...

On voit les threads en mode Waiting, la mémoire libre, le fault rate de la ram, le nombre d'appels systèmes et de switches de contexte (qui coûtent très cher en temps CPU). L'outil donne également la répartition entre user- et system-time du CPU. Normalement pour des applications Java, on devrait avoir peu de temps de cpu utilisé par le système. LA colonne "b" (="blocked") donne un indice supplémentaire.

Dans cet exemple, on voit que les theads attendent beaucoup des IO.

Maintenant, Dan reprend le discours.

Il montre VisualVM en train de monitorer un serveur Tomcat : used heap, nombre de classes chargées, utilisation des threads...

Voyons ce que nous apprend le thread dump, réalisé depuis visualvm. Beaucoup de threads sont en jaune, ce qui signifient qu'ils sont en attente (état Waiting).

Grâce à l'outil Thread Dump Analyzer, on peut analyser facilement les thread dumps générés par VisualVM ou par un kill -3 sur le process.

On s'aperçoit maintenant qu'il pourrait s'agir d'un problème d'IO bloquants. L'application utilise HttpClient pour appeler un service externe, et parser le résultat (en XML) grâce à XStream. La lenteur du service externe provoque la lenteur de notre service.
Le problème provient donc du service externe, et non de notre code. C'est donc cela qu'il faudra améliorer.

Sur un système standard client/serveur d'applications/base de données, on décuple le temps de réaction de chaque couche. Chaque couche peut avoir de bonnes performances (dans sa catégorie), mais il faut également mesurer le temps de communication inter-couches. Un driver JDBC est peut-être lent ?

Quand faut-il arrêter d'optimiser ?
Lorsque les "requirements" des clients sont atteints, mais pour cela il faut, encore une fois, savoir mesurer avant/après de manière répétable.

Kirk dit que beaucoup de docs (ou de rumeurs) disent que le Garbage Collector CMS apportera de meilleures performances, mais il faut douter de tous ces conseils non appuyés par des faits.

(Note personnelle : Terracotta déconseille fortement CMS)

Quand faut-il commencer à tester lors du développement d'une application ? Le premier but est d'obtenir des spécifications claires quant aux perfs attendues par les utilisateurs. Sans cela, les utilisateurs pourront toujours râler et les développeurs ne sauront jamais où ils en sont.

De même, il faut toujours résister à l'optimisation prématurée. Il faut d'abord développer un système qui fonctionne, puis ensuite l'améliorer ; mais cela ne fait jamais trop de mal de passer régulièrement jMeter sur le code pour éviter de passer à côté de gros bottlenecks.

Questions / Réponses

Question d'Antonio : En cas de crise, comment faire communiquer le DBA, le responsable réseau, et les développeurs ?

  • Une bonne bouteille de Scotch :)
  • Plus sérieusement, seules des mesures objectives permettent de ménager les susceptibilités et de résoudre les bons problèmes. Et aux managers d'aller voir le client et lui expliquer qu'on a trouvé d'où vient le problème et qu'on travaille dessus.

Question du public : est-ce que la mesure (JMeter...) peut perturber le fonctionnement normal de l'application ?
Mesurer peut effectivement augmenter la charge du système, mais si c'est de 1-5% et dans un but d'optimisation, c'est tolérable. Si cela atteint 10+%, cela peut fausser les mesures de manière trop importante.

Question : si tous les indicateurs sont déjà au vert, faut-il quand même optimiser ?
Non, c'est une perte de temps si les utilisateurs sont déjà contents du système (réactivité, mais aussi coût, etc.). Ce serait comme aller chez le médecin et lui dire hey, je me sens bien, mais pouvez-vous me faire sentir encore mieux ?
Il vaut mieux passer du temps à améliorer ses outils ou à développer d'autres use-cases métiers...

Question : quels sont les problèmes les plus fréquemment rencontrés ?
De nos jours, ce sont les problèmes de synchronisation / concurrence qui sont le plus fréquents. Le coût de la création d'objets en mémoire et de garbage collection ont été énormément réduits depuis les premières versions de Java, et ne sont plus un problème aujourd'hui.

Kirk dit que les architectures parallèles sont maîtrisées niveau hardware maintenant, mais que le problème persiste niveau soft. Certains langages permettent d'améliorer un peu la situation, comme Scala avec son modèle d'Acteurs, et Clojure.

Il reste encore pas mal de travail (en cours) en Java pour optimiser les I/O et avoir des constructs non-bloquants.

Utiliser des algorithmes sous forme de machines à états finis permet également d'éviter certains problèmes de concurrence, et Kirk pense qu'on les verra de plus en plus au cours des années à venir.

Question : quand faut-il réarchitecturer ?
Rarement, car on introduira alors d'autres bugs inconnus. Il vaut mieux travailler sur un système imparfait mais maîtrisé.

Dernier slide : "The end - you may now wake up your neighbour"


Commentaires

1. Le dimanche 24 janvier 2010, 13:04 par cfalguiere

Merci pour le compte rendu.

C'est bizarre la wave est buguée, je la suis sans la suivre et elle passe en état corrompu dès que j'essaie de l'éditer.

Pour répondre à la question de Julien, en dehors de JMeter pour relever les temps de réponse et des outils de la JVM cités par Olivier, il utilise les outils système Unix : vmstat et DTrace (Solaris seulement, ou en tout cas des UNIX/Linux payants), et quelques autres commandes UNIX j'imagine. Il a parlé de l'utilisation des logs aussi (Apache en particulier).
Le besoin à ce niveau de là est de savoir dans quelle direction partir pour affiner le diagnostic avec d'autres outils (Dans son exemple, l'analyse de thread dumps parce que l'absence de consommation de CPU et de RAM fait penser à des attentes de lock ou de réponse d'un composant externe)
C'est basique, mais en même temps quand tu interviens sur une production qui va mal, il y a en général peu d'outillage et on ne te laissera installer aucun outil qui nécessite une modification de l'application (ce que font tous les outils de profilage).
En plus ceux là tu les maîtrises et tu sais ce que tu lis même si l'information est un peu imprécise, alors que les compteurs applicatifs sont souvent différents d'une application (ou framework) à l'autre et trop ciblés dev pour le besoin.

J'ai quand même été un peu étonnée du silence sur JMX car les métriques sont relativement standardisées en J2EE, le support est embarqué dans le serveur d'applications et les outils de collecte sont peu invasifs. ça n'est pas toujours évident à obtenir mais on peut toujours légitimer que JMX est le protocole officiel de monitoring en J2EE.

Quand aux évidences qu'il a énoncées, je les répète aussi toute la journée, et dans beaucoup d'endroits ce ne sont pas encore des évidences.

2. Le dimanche 24 janvier 2010, 14:06 par Olivier Croisier

La Wave est ouverte au public en lecture seule ; seuls les participants explicitement inscrits dessus peuvent la modifier. Tu peux toujours m'envoyer ton ID GWave si tu souhaites que je t'ajoute, tu pourras alors directement répondre à Julien.

3. Le lundi 25 janvier 2010, 15:11 par Benoît

Olivier, le code javascript d'inclusion de la wave est inclus dans le contenu RSS du billet, et du coup, apparaît tel quel (en javascript) dans Google Reader, Netvibes, ...

4. Le mardi 26 janvier 2010, 16:11 par HollyDays

Je plussoie Claude : je rencontre moi aussi régulièrement des développeurs (et des architectes !) qui optimisent à l'instinct au lieu d'optimiser suite à des mesures de temps d'exécution (même imparfaits). Et nombre d'entre eux ont la tête dure, et n'acceptent pas facilement que leur instinct puisse se tromper (voire que certaines de leurs optimisations puissent, non seulement ne pas accélérer, mais en plus ralentir l'application !).

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.