J’ai passé trois excellentes journées à Devoxx France. A l’aide d’un article par journée de conférence, je vais essayer de résumer mon Devoxx à moi, comment j’ai vécu ces trois jours et ce que j’y ai appris.
La journée du mercredi était consacrée aux universités et ateliers. D’une durée de 3 heures, ces sessions permettent de rentrer en profondeur dans un sujet. De mon côté, j’ai choisi de faire une journée Java 8 et je n’ai vraiment pas été déçu !
Tout ce que vous avez toujours voulu savoir sur les lambdas et plus encore
La journée commence avec une université de 3 heures menée de main de maître (de conférence) par Remi Forax qui nous présente les lambdas de Java 8 en commençant par montrer que Java est un langage qui évolue. Il prend pour cela un morceau de code écrit en Java 1.0 et montre comment il a été simplifié et amélioré au fil des différentes versions. A chaque nouvelle version, le nombre de lignes de code diminue, la lisibilité et l’élégance sont de plus en plus au rendez-vous.
Il explique que les lambdas ont mis du temps à arriver parce qu’elles ont fait couler beaucoup d’encre. En 2006, les gens qui s’occupent de la spécification de Java (James Gosling et ses copains) ont fait une proposition. Ceux qui écrivent le SDK Java (Josh Bloch et sa bande) en ont fait une autre. Enfin, un groupement d’utilisateurs de Java a fait une proposition encore différente. Problème : ceux qui spécifient le langage ne sont pas d’accord avec ceux qui l’implémentent, et de toute façon aucune de ces deux solutions ne convenait aux utilisateurs. Cela a donné lieu à la guerre des lambdas. C’est finalement un compromis qui a été choisi. Le compromis a pris en compte les différents points de vue. L’idée était également de faire en sorte de pouvoir utiliser les lambdas dans des API qui n’étaient pas conçues pour cela, et notamment tout ce qui a été écrit dans les versions précédentes de Java. C’est entre autres cela qui a fait qu’il a été décidé que les lambdas seraient capables d’implémenter toute interface fonctionnelle (c’est-à-dire qui n’a qu’une seule méthode abstraite). Elles sont ainsi utilisables partout là où avant on créait des classes anonymes.
L’intégration des lambdas a nécessité d’enrichir certaines API et notamment celle des collections. Le problème c’est que les interfaces de l’API collection sont implémentées ailleurs que dans le JDK. Il suffit de rajouter une méthode dans une interface pour casser la compilation de tous ceux qui ont implémenté des collections. Peu acceptable… Il faut donc trouver une solution. C’est de là que vient la notion d’implémentation par défaut dans les interfaces. Mais il fallait que la compatibilité soit assurée sans recompiler, simplement au niveau bytecode. Cela a posé de gros problèmes qu’il a nous a brillamment exposés.
Enfin, il nous a expliqué que les lambdas ne sont pas des classes anonymes. Elles sont bien moins coûteuses en performance. C’est ainsi qu’il nous a montré un magnifique micro benchmark d’un morceau de code qui utilise une lamdba. Il nous avait prévenu que ce benchmark était faux. Ce qui était intéressant c’est de savoir pourquoi il est faux. En fait, quand on fait un benchmark il faut se méfier de la JVM qui se permet de faire des optimisations parfois très radicales, et ça peut aller jusqu’à ne pas exécuter du tout le code du benchmark si elle s’aperçoit par exemple que le résultat est utilisé nulle part. Pour nous montrer tout cela il a d’abord ajouté une option qui demande à la JVM de montrer les optimisations qu’elle fait, puis il nous a fait plonger dans le bytecode Java de la lambda avec l’utilisation du fameux invokedynamic. Il a terminé par une plongée dans le monde de l’assembleur, ce qui m’a rappelé au passage combien il est agréable de ne pas avoir à en faire au quotidien, mais c’était pour autant très intéressant !
Merci Rémi, c’était super. Ça fait plaisir de voir qu’en France on a de tels talents. Je ne sais pas si les étudiants à qui Rémi enseigne le Java mesurent la chance qu’ils ont !
Java 8 Streams & Collectors : patterns, performances, parallélisation
José Paumard nous fait découvrir les richesses de la toute nouvelle API stream de Java 8. On commence avec une bonne heure de présentation théorique ; il faut bien cela tellement l’API stream est riche. On se rend vite compte que l’apparition des lambdas dans Java 8 a ouvert les portes de la programmation fonctionnelle, et que l’ensemble du JDK a été revu pour s’appuyer pleinement dessus, l’API Collections n’étant pas en reste. L’API stream est une nouvelle API complémentaire à celle des collections qui permet de traiter les éléments d’une collection comme un flux d’éléments, de les filtrer, les transformer, les réduire, les grouper et plein d’autre choses, le tout en parallélisant si il le faut. Il a d’ailleurs pris pas mal de temps pour expliquer dans quels cas la parallélisation était possible (parfois elle n’a pas de sens et conduit à des erreurs de calcul), et également dans quels cas elle était intéressante en terme de performance.
Il enchaîne avec une session de live coding dans laquelle il prend des exemples d’ensemble de données comme la liste des Mac Donald’s des États-Unis et il nous montre qu’avec quelques lignes de code on peut faire des calculs très intéressants et trouver en quelques lignes que c’est à Houston qu’il y a le plus de restaurants Mac Donald’s aux États-Unis. Il poursuit avec d’autres exemples comme le calcul des points au jeu du scrabble. Cela devenait de plus en plus compliqué alors que ma concentration diminuait progressivement, sur la fin je dois bien avouer que je n’ai pas tout compris. Je crois que je n’ai pas été le seul et je pense que cela nous aura également appris quelque chose : la programmation fonctionnelle, c’est super, ça permet de faire beaucoup de choses en peu de lignes, mais le code devient assez vite compliqué. A utiliser avec modération donc !
La syntaxe des références vers une méthode n’est pas très habituelle pour un développeur Java. Cela s’écrit comme ceci : String::isEmpty. A chaque fois qu’il en écrivait une, il disait « vous vous y ferez ». A tel point qu’il s’est fait vanner en direct sur Twitter à ce sujet par Guillaume Laforge.
Le leitmotiv de @JosePaumard : les method references, vous vous y ferez ! #devoxxfr #stream8 pic.twitter.com/MQzVN3xxPo
— Guillaume Laforge (@glaforge) April 16, 2014
Les slides de cette présentation sont disponibles ici.
Microbenchmarking avec JMH
Intrigué par tout ce qu’avait expliqué Rémi Forax le matin, je suis ensuite allé voir Henri Tremblay nous faire découvrir avec son bel accent québécois un outil qui permet de faire du microbenchmarking en Java. Le microbenchmarking consiste à mesurer le temps d’exécution de portions de code très petites.
JMH est un outil qui est utilisé par les développeurs du JDK pour mesurer les performances de l’API. Le problème lorsqu’on mesure manuellement les performances d’un petit morceau de code, c’est que la JVM comprend assez vite comment il fonctionne et se permet de le simplifier, voire même de ne même pas l’exécuter si elle se rend compte par exemple que la valeur de retour n’est pas utilisée et qu’il n’a aucune interaction avec l’environnement.
JMH est un outil basé sur les annotations qui permet d’empêcher la JVM de faire des optimisations. Il utilise pour cela des astuces très vicieuses et variées. Henri nous a montré qu’il fallait toujours comparer les résultats qu’on obtient à un benchmark de référence, celui d’une fonction qui ne fait rien, histoire de s’assurer par exemple qu’on ne va pas plus vite qu’un code qui ne fait rien, ce qui peut être le cas si l’optimisation de la JVM passe par là. Sur sa machine qui était capable de traiter théoriquement 3,6 milliards d’instructions par seconde, il arrivait à 2,9 milliards de lancements d’une fonction qui ne fait rien, ce qui est plutôt cohérent.
Il a ensuite commencé à vraiment faire son benchmark qui consistait à comparer la vitesse d’EasyMock (auquel il contribue) et de Mockito. A la fin du benchmark avec JMH on avait des résultats bien différents qu’avec le premier benchmark qu’il avait écrit à la main avec une simple boucle for. Avant de conclure qu’EasyMock est bien plus rapide que Mockito, ce qui pourtant était visiblement le cas d’après les résultats, il nous a dit de s’assurer de ne pas conclure trop vite et de bien préciser les conditions du benchmark et surtout ce qu’il mesure. Il mesurait en effet la création de mock d’une classe, et il s’avère qu’EasyMock est bien plus performant dans ce cas, par contre Mockito est bien plus rapide pour créer des mocks d’interface. Méfions-nous des résultats des benchmarks !
Je me suis posé deux questions quand même. Déjà ne peut-on pas demander à la JVM de ne rien optimiser ? On a coutume de dire que qui peut le plus peut le moins ! Sinon je me suis également demandé pourquoi vouloir mesurer le temps d’exécution sans optimisation dans la mesure où les optimisations seraient également présentes à l’exécution, mais Rémi Forax y avait déjà répondu : la JVM optimise un morceau de code en fonction de son environnement, en fonction du nombre de fois qu’il est utilisé et bien d’autres paramètres également, ce qui fait qu’on ne peut pas trop compter sur le fait que l’optimisation sera vraiment faite ou faite de la même manière à l’exécution de la vraie application qui embarque ce code. L’idée est donc semble-t-il de mesurer dans le pire cas, c’est-à-dire sans aucune optimisation.
REST facile et robuste avec Vert.x et Groovy
Je suis allé voir cette conférence dans l’espoir de trouver une alternative à Play Framework. J’ai malheureusement assez vite décroché, ne serait-ce déjà par le manque de rythme et de dynamisme du speaker, mais également parce que le code qu’il nous montrait ne me faisait pas vraiment rêver. Déjà parce que c’était du Groovy très dynamique avec aucun type et la syntaxe minimale, le code était donc pour moi assez peu lisible et robuste. Mais ce qui m’embête le plus dans un tel framework, c’est son aspect entièrement asynchrone et event-driven. Même si en terme de performance c’est sans doute la meilleure architecture, j’ai de sérieux doutes sur le fait que ce soit vraiment testable et facile à aborder pour quelqu’un de non initié à cela. Je suppose également que cela implique un couplage fort de l’ensemble du code de l’application au mécanisme d’événements. De l’expérience que j’ai avec Play Framework, le code asynchrone est plus difficile à tester, il est également plus difficile à lire, surtout sans Java 8 et plus précisément les lambdas. Je ne suis donc pas encore vraiment convaincu par la programmation event-driven.
Pour ce qui concerne Groovy, il faut noter que vert.x est compatible avec tous les langages qui tournent dans une JVM, que ce soit Java, Jython, JRuby, Javascript etc… On n’est donc pas obligé de faire du Groovy.
One Java library to rule all security protocols
J’ai terminé la journée par la présentation de pac4j par son créateur. Cette bibliothèque orientée web permet de se connecter de manière générique à un ensemble de systèmes d’authentification tels que OAuth, Open ID et bien d’autres. L’idée est qu’on utilise une seule API et la bibliothèque fournit des connecteurs vers différents mécanismes.
Différents connecteurs sont également fournis pour les frameworks web les plus connus, et même pour Play, qui a pourtant un fonctionnement un peu particulier.
L’intérêt d’une telle bibliothèque est évident, je m’interroge simplement sur la maturité de ce projet qui ne semble pas encore être arrivée à terme.