Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Julia s'ouvre au parallélisme multifil composable avec sa version 1.3
La voie royale pour exploiter au mieux tous les cœurs disponibles

Le , par dourouc05

31PARTAGES

17  0 
Lors de la sortie de la première préversion de Julia 1.3, les développeurs se sont montrés fort peu diserts en ce qui concerne les vraies fonctionnalités qui sont arrivées. Les notes de version montraient principalement qu'une très bonne partie de la bibliothèque standard était sûre dans un contexte multifil, mais la vraie bonne nouvelle est que Julia gère désormais le multifil comme prévu dès le début de la conception du langage (l'objet Task était disponible bien avant la première version publique de Julia). Tous les outils nécessaires pour effectuer des calculs à très grande échelle sont donc disponibles : avec plusieurs processus, de manière distribuée sur un réseau, sur des cartes graphiques, mais aussi avec plusieurs fils d'exécution dans un même processus (la documentation n'est pas encore vraiment à jour).

En quoi consiste donc cette nouvelle fonctionnalité ? Julia implémente une forme de parallélisme de tâches : tout morceau de code peut être marqué comme parallélisable ; lors de son exécution, il est lancé dans un autre fil d'exécution. Un ordonnanceur dynamique gère l'exécution de tous ces fils d'exécution. Par exemple, ce morceau de code calcule la suite de Fibonacci de manière récursive et très inefficace, mais surtout sur autant de processeurs que l'on souhaite (si on demande un élément suffisamment éloigné dans la suite) :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
import Base.Threads.@spawn
 
function fib(n::Int)
    if n < 2
        return n
    end
    t = @spawn fib(n - 2)
    return fib(n - 1) + fetch(t)
end
La chose importante à remarquer est @spawn : cette commande permet de lancer un bout de code sur un autre fil d'exécution. Le résultat n'est pas immédiatement retourné, mais bien une référence qui permet de le récupérer une fois qu'il est disponible (à travers fetch).

Derrière, l'ordonnanceur fait en sorte que l'exécution soit la plus efficace possible, peu importe le nombre de tâches qu'on lui demande d'exécuter. Il est prévu pour monter à plusieurs millions de tâches à effectuer, de telle sorte que le programmeur est vraiment libéré de tous les détails pratiques (lancer, synchroniser des fils d'exécution). L'implémentation fonctionne aussi de manière composante : à l'intérieur d'une tâche exécutée en parallèle, on peut appeler des fonctions qui, elles-mêmes, font appel à des tâches parallèles. Toutes ces tâches sont ordonnancées de manière efficace, de telle sorte qu'on ne doive pas se demander comment chaque fonction est codée.

Jusqu'à présent, en Julia, quand on appelait une fonction qui pouvait exploiter une forme de parallélisme à l'intérieur d'une section parallèle, on pouvait très vite remarquer une dégradation de la performance. L'explication est un conflit de ressources (oversubscription) : l'application veut se lancer sur bien plus de cœurs que disponible. Par exemple, sur un ordinateur à quatre cœurs, on divise un gros problème en quatre morceaux équivalents, chacun devant résoudre un gros système linéaire : cette dernière opération peut également se paralléliser de manière efficace ; jusqu'à présent, avec Julia (ou OpenMP, ou encore bon nombre d'autres environnements de programmation parallèles), seize fils d'exécution se chargent de résoudre quatre systèmes linéaires — sur quatre cœurs. Pour améliorer les temps de calcul, il vaut mieux alors désactiver le parallélisme au niveau des systèmes linéaires ! Le nouveau système, entièrement composable, gère ces situations en ne laissant que quatre fils s'exécuter en même temps.

Julia dispose aussi d'une série d'outils bien pratiques : des sémaphores, des verrous, du stockage par fil d'exécution (notamment utilisé pour la génération de nombres aléatoires : chaque fil dispose de son propre objet RNG).

En coulisses, ce système n'est pas d'une simplicité déconcertante à gérer. Les tâches de Julia ne sont pas implémentées de la même manière pour chaque système d'exploitation : Windows dispose de fibres, conceptuellement très proches, mais pas Linux (certaines bibliothèques implémentent ce genre de mécanisme, cependant). Chaque tâche nécessite sa propre pile d'appels de fonction pour gérer l'exécution, mais elle doit être gérée de manière particulière : le système d'exploitation se charge d'énormément de choses pour passer l'exécution d'un fil à l'autre, mais, en ce qui concerne les tâches, les applications doivent implémenter beaucoup de choses elles-mêmes. Pour le moment, Julia considère qu'une tâche ne peut s'exécuter que dans un seul fil d'exécution, sans possibilité de passer sur un autre fil pour mieux exploiter le processeur, mais cela arrivera dans le futur.

L'implémentation actuelle est complète d'un point de vue Julia, mais a une limitation majeure : les bibliothèques appelées par Julia (par exemple, pour la résolution de systèmes linéaires) sont écrites en C et ne gèrent pas nativement cette forme de parallélisme. Ce sera corrigé dans une version à venir. L'API n'est pas encore définitive et pourrait légèrement évoluer avant d'être entièrement stabilisée. La gestion des entrées-sorties n'est pas encore au point : un verrou global est imposé sur ces opérations, mais cela devrait changer. Une fois ces éléments améliorés, la bibliothèque standard pourrait commencer à exploiter le parallélisme de tâches de manière interne, par exemple pour trier des tableaux suffisamment grands.

Source : Announcing composable multi-threaded parallelism in Julia.

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de Matthieu76
Membre éclairé https://www.developpez.com
Le 08/08/2019 à 11:16
J'ai à peine lu le titre que ça m'a donné mal au crâne
0  0