Après 3 betas et 4 release candidates, la version 1.8 de Julia est enfin sortie. L'équipe de développement a annoncé le 18 août la sortie de la version 1.8 de Julia. Cette version Julia 1.8 apporte le profilage des threads et des tâches, comprend des améliorations du support pour Apple Silicon, une temporisation de la charge du paquet, un nouveau planificateur par défaut pour @threads.La dernière version de correction de bogues mineurs, 1.7, a été publiée le 2 Decembre. Cette version corrigeait les erreurs de synchronisation, affinait le support de l'ordonnancement des charges de travail sur plusieurs threads, avec le générateur de nombres aléatoires par défaut plus convivial pour les threads, et ajoutait les atomiques comme caractéristique du langage primitif.
Julia est un langage de programmation open source et un écosystème pour le calcul scientifique de haute performance. Conçu par des chercheurs du MIT en 2009 et dévoilé pour la première fois au grand public en 2012, il est doté d’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. Voici, ci-dessous, quelques améliorations apportées à Julia dans la version 1.8 :
const sur les champs dans les structs mutables
Julia supporte maintenant l'annotation de champs individuels d'une structure mutable avec des annotations const :
| Code : | Sélectionner tout |
1 2 3 4 | mutable struct T
x::Int
const y::Float64
end |
qui fait que le champ y est constant (et ne peut donc pas être réaffecté après la création du type). Cela peut être utilisé pour faire respecter des invariants, mais le compilateur peut également en tirer parti pour améliorer le code généré.
Site d'appel @inline
Avant Julia 1.8, la macro @inline ne pouvait être utilisée que sur les définitions de méthodes et la fonction était inlined à tous les sites d'appel de cette méthode. Cependant, il peut être utile de faire le choix pour un site d'appel donné si une fonction doit être inlined. C'est pourquoi il est désormais possible d'appliquer la macro @inline à un site d'appel donné, comme @inline f(x), qui indiquera au compilateur d'inclure la méthode à cet appel spécifique.
Les variables globales non constantes dans Julia ont une pénalité de performance parce que le compilateur ne peut pas raisonner sur leur type puisqu'elles peuvent être réassignées à un autre objet d'un autre type pendant l'exécution. Dans Julia 1.8, il est maintenant possible de spécifier le type d'une variable globale non constante en utilisant la syntaxe x::T où T est le type de la variable globale. Essayer de réassigner la variable à un objet d'un autre type provoque des erreurs :
| Code : | Sélectionner tout |
1 2 3 4 5 6 | julia> x::Int = 0 0 julia> x = "string" ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64 ... |
L'annotation de type des variables globales supprime une grande partie (mais pas la totalité) du coût de l'utilisation de variables globales non constantes.
Nouveau planificateur par défaut pour @threads
Julia a eu la macro @threads pour paralléliser une boucle for même avant que le runtime générique de tâches parallèles soit introduit dans Julia 1.3. En raison de cette raison historique, @threads a fourni un ordonnancement statique pour éviter de casser les programmes qui s'appuient accidentellement sur ce comportement strict (voir :static scheduling dans la documentation). Ainsi, pour travailler agréablement avec le reste du système multitâche, l'ordonnanceur :static a été introduit dans Julia 1.5 pour aider les gens à se préparer à changer le comportement d'ordonnancement par défaut dans le futur, c'est-à-dire maintenant. Dans Julia 1.8, les programmes écrits avec @threads peuvent pleinement exploiter l'ordonnanceur dynamique et composable.
Pour illustrer l'amélioration, considérez le programme suivant qui simule une charge de travail intensive pour le CPU et qui nécessite quelques secondes pour se terminer :
| Code : | Sélectionner tout |
1 2 3 4 5 | julia> function busywait(seconds)
tstart = time_ns()
while (time_ns() - tstart) / 1e9 < seconds
end
end; |
Avant Julia 1.8, @threads utilise toujours tous les threads des travailleurs. En tant que tel, @threads ne se termine pas avant que toutes les tâches précédentes soient terminées :
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 | julia> @time begin
Threads.@spawn busywait(5)
Threads.@threads for i in 1:Threads.nthreads()
busywait(1)
end
end
6.003001 seconds (16.33 k allocations: 899.255 KiB, 0.25% compilation time) |
Le temps d'exécution est d'environ 6 secondes. Cela signifie qu'une tâche créée en interne pour @threads attend que la tâche busywait(5) soit terminée. Cependant, dans Julia 1.8, nous avons :
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 | julia> @time begin
Threads.@spawn busywait(5)
Threads.@threads for i in 1:Threads.nthreads()
busywait(1)
end
end
2.012056 seconds (16.05 k allocations: 883.919 KiB, 0.66% compilation time) |
Cela prend 2 secondes car l'un des threads non occupés peut exécuter deux des itérations d'une seconde pour terminer la boucle for.
Profiler
Nouveau profileur d'allocation
Les allocations inutiles dans le tas peuvent sérieusement dégrader les performances, et les outils existants pour les traquer (à savoir @time et --track-allocation) ne fournissaient pas tout à fait les détails fins, la bonne visualisation et la facilité d'utilisation que nous recherchions. Nous avons donc créé le profileur d'allocation (Profile.Allocs), qui capture les...
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.