
et supprime les recompilations inutiles
Les responsables du langage Julia ont annoncé la disponibilité de Julia 1.6 le 24 mars. Elle est présentée comme étant une exception, car elle est susceptible de devenir la prochaine version de support à long terme (LTS) de Julia. De plus, la version a été testée pour les régressions par rapport à tous les paquets open source enregistrés et les problèmes découverts ont été corrigés. La décision finale quant à savoir si Julia 1.6 deviendra la prochaine version de support à long terme sera prise après qu'elle ait été testée par les développeurs.
Julia est un langage de programmation conçu par des chercheurs du MIT en 2009 et dévoilé pour la première fois au grand public en 2012. C’est un langage de programmation de haut niveau, performant et dynamique pour le calcul scientifique, avec une syntaxe familière aux utilisateurs d'autres environnements de développement similaires. Julia connaît une croissance fulgurante depuis sa sortie et certains vont même jusqu’à dire qu’il s’agit du langage idéal pour le calcul scientifique, la science des données et les projets de recherche. Le langage s'est popularisé lorsque le projet est devenu open source en 2012. Il est actuellement disponible sous licence MIT.
À la base, ses concepteurs voulaient un langage avec une licence libre et renfermant de nombreux avantages surtout pour la communauté scientifique. « Nous voulons un langage open source, avec une licence libre. Nous voulons un langage qui associe la rapidité de C et le dynamisme de Ruby. En fait, nous voulons un langage homoïconique, avec de vraies macros comme Lisp et avec une notation mathématique évidente et familière comme MATLAB. Nous voulons quelque chose d’aussi utilisable pour la programmation générale que Python, aussi facile pour les statistiques que R, aussi naturel pour la gestion de chaîne de caractères que Perl, aussi puissant pour l’algèbre linéaire que Matlab et aussi bien pour lier des programmes que le shell. Nous voulons qu’il soit à la fois interactif et compilé », avaient-ils déclaré en 2012.
La plupart des versions de Julia sont programmées dans le temps et ne sont donc pas planifiées autour de fonctionnalités spécifiques, mais selon les responsables du projet Julia, la version 1.6 est une exception, car elle est susceptible de devenir la prochaine version LTS de Julia. Pour cette raison, ils ont pris plus de temps pour développer la version afin de s'assurer que les fonctionnalités qui sont nécessaires pour l’évolution de l'écosystème soient incluses dans la version. De plus, la version a été testée pour les régressions par rapport à tous les paquets open source enregistrés et les problèmes ont été recherchés et corrigés. Voici, ci-dessous, quelques-unes des améliorations et fonctionnalités apportées par la version 1.6 de Julia.
Précompilation parallèle
L'exécution de toutes les instructions d'un module implique souvent la compilation d'une grande quantité de code, Julia crée des caches précompilés du module pour réduire ce temps. Dans la version 1.6, cette précompilation du module est plus rapide et se produit avant que la fin du mode pkg>. Jusqu'à présent, la précompilation se déroulait uniquement sous la forme d’une séquence de traitement unique, précompilant les dépendances une par une lorsque cela était nécessaire pendant le processus de chargement du code linéaire lorsqu'un paquet est utilisé ou importé pour la première fois. Par le passé, avec la version 1.5 et inférieur, si on prend l’exemple des équations différentielles, avec DifferentialEquations.jl.
Code : | Sélectionner tout |
1 2 3 4 5 | (v1.5) pkg> add DifferentialEquations ... julia> @time using DifferentialEquations [ Info: Precompiling DifferentialEquations [0c46a032-eb83-5123-abaf-570d42b7fbaa] 474.288251 seconds … |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 | (v1.6) pkg> add DifferentialEquations ... Precompiling project... Progress [========================================>] 112/112 112 dependencies successfully precompiled in 72 seconds julia> @time using DifferentialEquations 4.995477 seconds … |
Les erreurs pendant la précompilation seront affichées seulement pour les paquets énumérés dans le projet, afin d'éviter de précompiler des dépendances qui sont simplement citées et non utilisées dans l'environnement. Le processus de précompilation automatique se souviendra s'il a échoué pour un paquet dans l'environnement donné et ne réessayera pas jusqu'à ce qu'il change. La précompilation automatique peut être gracieusement interrompue avec ctrl-c.
Temps de compilation
Selon les concepteurs de Julia, il s’agit d’un petit changement qui devrait aider les nouveaux venus à comprendre l'une des bizarreries de Julia. Désormais, la macro de synchronisation @time et la version détaillée @timev indiquent désormais si une partie du temps rapporté a été consacrée à la compilation.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 | julia> x = rand(10,10); julia> @time x * x; 0.540600 seconds (2.35 M allocations: 126.526 MiB, 4.43% gc time, 99.94% compilation time) julia> @time x * x; 0.000010 seconds (1 allocation: 896 bytes) |
Notons que dans certains cas, le système regarde à l'intérieur de l'expression @time et compile une partie du code appelé avant que l'exécution de l'expression de niveau supérieur ne commence. Lorsque cela se produit, une partie du temps de compilation ne sera pas comptabilisée. Pour inclure ce temps, on peut lancer @time @eval.
Suppression des recompilations inutiles
Une des caractéristiques les plus puissantes de Julia est son extensibilité : on peut ajouter de nouvelles méthodes à des fonctions précédemment définies, et utiliser des méthodes précédemment définies sur de nouveaux types. Parfois, ces nouvelles entités obligent Julia à recompiler le code pour tenir compte des changements dans la version. Cela se produit en deux étapes : d'abord, le code « obsolète » est invalidé, le marquant comme impropre à l'utilisation ; ensuite, si nécessaire, le code est à nouveau compilé à partir de zéro en tenant compte des nouvelles méthodes et des nouveaux types.
Les versions précédentes de Julia étaient quelque peu conservatrices, et invalidaient l'ancien code dans certaines circonstances où il n'y avait pas de changement réel dans la version. De plus, il y avait de nombreux endroits où Julia et ses bibliothèques standards étaient écrites d'une manière qui ne permettait pas l'inférence de type de Julia. Étant donné que le compilateur devait parfois invalider du code juste parce qu'une nouvelle méthode pouvait s'appliquer, toute incertitude sur les types amplifie le risque et la fréquence d'invalidation. Dans les anciennes versions de Julia, la combinaison de ces effets rendait l'invalidation très répandue : le simple chargement de certains paquets entraînait l'invalidation de 10 % du code précompilé de Julia. Le délai de recompilation pouvait parfois rendre les sessions interactives plus lentes.
Dans la version 1.6, le schéma d'invalidation de l'ancien code a été rendu plus précis et sélectif. De plus, Julia et ses bibliothèques standards ont fait l’objet d’une refonte complète pour aider l'inférence de type à arriver plus souvent à une réponse concrète. Le résultat est un langage Julia plus léger, plus rapide et considérablement plus réactif et agile dans les sessions interactives, surtout grâce à une invalidation de méthodes plus précise.
Réduction de la latence à la compilation
Dans cette version, il n'y a pas d'avancée majeure en ce qui concerne la réduction de la latence des compilateurs, mais des améliorations modestes ont été apportées grâce au travail sur la structure de données des tables de méthodes. Les méthodes ambiguës ont également été identifiées lors de l'insertion, dans l'espoir d'éviter de répéter le travail pour chaque future requête. Malheureusement, le tri d'un ordre partiel nécessite un temps quadratique, et ce temps se manifeste de manière proéminente pendant le chargement du paquet (lorsque les méthodes d'un paquet doivent être insérées dans les tables de méthodes actives).
Une amélioration a été apportée en déplaçant le tri et la détection des ambiguïtés dans l'algorithme de recherche des méthodes correspondantes. Cet algorithme s'exécutant très souvent, les concepteurs ont préalablement pensé qu’il n’était pas utile. L'essentiel étant que la grande majorité des requêtes portent sur des types suffisamment spécifiques pour que la plupart des correspondances possibles puissent être éliminées facilement, ce qui réduit considérablement le nombre d'entrées pour les étapes les plus coûteuses.
La principale amélioration visible ici concerne le chargement des paquets, ce qui ajoute un peu de vitesse supplémentaire en plus des gains obtenus en traitant les invalidations. Des efforts considérables ont été consentis pour affiner les caractéristiques de qualité d’inférence, à la fois en arrêtant rapidement l'analyse lorsqu'elle n'est pas perçue comme n’étant pas utile et en extrayant des informations plus précises lorsque cela est possible. Ces deux aspects peuvent avoir des avantages significatifs pour les codes complexes, tels que les bibliothèques de traçage, qui se ramifient sur un grand nombre d'options de configuration différentes.
Accélération du chargement
Bien que la stratégie de Julia ait toujours été de donner la priorité à la fiabilité et à la reproductibilité sur toutes les autres préoccupations, pour les concepteurs, dans le passé, cela a eu un coût. La solution aux problèmes de fiabilité et de reproductibilité était d'isoler plus complètement les binaires installés et de les compiler en utilisant le framework BinaryBuilder.jl. Les bibliothèques construites à partir de BinaryBuilder.jl sont le plus souvent utilisées par le biais de paquets dits JLL qui fournissent une API normalisée que les paquets Julia peuvent utiliser pour accéder aux binaires fournis.
Cette facilité d'utilisation et la fiabilité de l'installation ont entraîné une augmentation considérable des temps de chargement par rapport au vieux temps où les paquets Julia devaient aveuglément charger toutes les bibliothèques qui se trouvaient sur le chemin de recherche des bibliothèques. Pour illustrer le problème, dans Julia 1.4, le chargement de la pile GTK+3 nécessitait 7 secondes alors qu'il prenait environ 500 ms sur la même machine dans les versions précédentes. Après de nombreux mois de travail acharné et de recherches minutieuses, les concepteurs de Julia ont annoncé avec beaucoup de bonheur que la même pile de bibliothèques prend maintenant moins de 200 ms à charger en utilisant Julia v1.6 sur la même machine.
Le travail consenti pour réduire la taille des paquets JLL a abouti à la création d'un nouveau paquet, JLLWrappers.jl. Ce paquet fournit des macros qui génèrent automatiquement les liaisons nécessaires à un paquet JLL et ce en utilisant le nombre minimum de fonctions et de structures de données possible.
En limitant le nombre de...
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.