Julia

From CC Doc
Jump to navigation Jump to search
This page is a translated version of the page Julia and the translation is 100% complete.
Other languages:
English • ‎français

Julia est un langage de programmation conçu pour être performant, facile d'utilisation et portable. Sur les grappes de Calcul Canada, vous pouvez l'utiliser en installant un module.

Compiler des paquets

La première fois que vous ajoutez un paquet à un projet Julia (avec Pkg.add ou en mode paquet), le paquet ajouté sera téléchargé, installé dans ~/.julia et précompilé. Le même paquet peut être ajouté à plusieurs projets, auquel cas les données dans ~/.julia seront réutilisées. Des versions différentes d'un même paquet peuvent être ajoutées à des projets différents; les versions des paquets requises coexisteront dans ~/.julia. En comparaison avec Python, Jles projets Julia remplacent les environnements virtuels en évitant la duplication du code.

À partir de Julia 1.6, les paquets Julia incluent leurs dépendances binaires, par exemple les bibliothèques. Il n'est donc pas nécessaire de charger un module logiciel, ce qui de toute façon n'est pas recommandé.

Jusqu'à Julia 1.5, des problèmes pourraient survenir si un paquet dépend de binaires fournis par le système. Par exemple, JLD dépend d'une bibliothèque HDF5 fournie par le système. Sur un ordinateur personnel, Julia tentera d'installer une telle dépendance en utilisant yum ou apt avec sudo. Ceci ne fonctionnera pas sur une grappe de Calcul Canada; il faudra plutôt fournir des informations supplémentaires pour permettre au gestionnaire de paquet de Julia (Pkg) de trouver la bibliothèque HDF5.

$ module load gcc/7.3.0 hdf5 julia/1.4.1
$ julia
julia> using Libdl
julia> push!(Libdl.DL_LOAD_PATH, ENV["HDF5_DIR"] * "/lib")
julia> using Pkg
julia> Pkg.add("JLD")
julia> using JLD

En supprimant la ligne Libdl.DL_LOAD_PATH dans cet exemple, il n'y aurait pas de problème sur Graham parce que la bibliothèque HDF5 est installée pour tout le système, ce qui n'est pas le cas avec Cedar. La meilleure solution pour tous les systèmes de Calcul Canada est donc d'utiliser le contenu de l'exemple. Chargez d'abord le module approprié et utilisez la variable d'environnement définie par le module (ici HDF5_DIR) pour étendre Libdl.DL_LOAD_PATH. Ceci fonctionne de la même manière sur tous les systèmes.

Quotas de fichiers et de stockage

Dans l'exemple ci-dessus, le fait d'installer seulement le paquet JDL crée une arborescence ~/.julia de 18673 fichiers et répertoires et utilise 236Mo d'espace, ce qui représente environ 5% du quota de /home pour un utilisateur standard. Il faut se souvenir que l'installation de plusieurs paquets consomme beaucoup d'espace.

Versions disponibles

Nous avons retiré les versions antérieures à 1.0 parce que l'ancien gestionnaire de paquets créait plusieurs petits fichiers, ce qui affectait la performance des systèmes de fichiers parallèles. Veuillez utiliser Julia 1.4.1 ou une version plus récente.

Question.png
[name@server ~]$ module spider julia
--------------------------------------------------------
  julia: julia/1.4.1
--------------------------------------------------------
[...]
    You will need to load all module(s) on any one of the lines below before the "julia/1.4.1" module is available to load.

      nixpkgs/16.09  gcc/7.3.0
[...]
Question.png
[name@server ~]$ module load gcc/7.3.0 julia/1.4.1

Porter du code de Julia 0.x à 1.x

Les développeurs de Julia ont stabilisé l'API et supprimé des fonctionnalités obsolètes; ces modifications se retrouvent dans la version 1.0 lancée à l'été 2018. La version 0.7.0 a aussi été lancée pour faciliter la mise à jour des programmes. Julia 0.7.0 contient toutes les fonctionnalités de 1.0 en plus des fonctionnalités obsolètes des versions 0.x; des avertissements de dépréciation sont produits à l'utilisation. Le code qui est exécuté sans avertissement par Julia 0.7 devrait être compatible avec Julia 1.0.

Travailler avec plusieurs processus sur une grappe

Dans l'exemple suivant, du code parallèle Julia est utilisé pour calculer pi avec 100 cœurs sur les nœuds d'une grappe.


File : run_julia_pi.sh

#!/bin/bash
#SBATCH --ntasks=100
#SBATCH --cpus-per-task=1
#SBATCH --mem-per-cpu=1024M
#SBATCH --time=0-00:10

srun hostname -s > hostfile
sleep 5
julia --machine-file ./hostfile ./pi_p.jl 1000000000000


Dans cet exemple, la commande

srun hostname -s > hostfile

génère une liste des noms des nœuds alloués et ajoute cette liste au fichier texte hostfile. Ensuite, la commande

julia --machine-file ./hostfile ./pi_p.jl 1000000000000

démarre un processus Julia principal et 100 processus de travail sur les nœuds spécifiés dans hostfile et lance le programme pi_p.jl en parallèle.

Interface MPI

Assurez-vous que la MPI de Julia est configurée pour utiliser nos bibliothèques MPI. Pour une installation correcte, employez le code suivant :

module load StdEnv/2020  julia/1.5.2
export JULIA_MPI_BINARY=system
export JULIA_MPI_PATH=$EBROOTOPENMPI
export JULIA_MPI_LIBRARY=$EBROOTOPENMPI/lib64/libmpi.so
export JULIA_MPI_ABI=OpenMPI
export JULIA_MPIEXEC=$EBROOTOPENMPI/bin/mpiexec

Lancez ensuite Julia puis, à partir de Julia, lancez

import Pkg;
Pkg.add("MPI")
using MPI

Pour l'utiliser par la suite, lancez (pour deux processus)

module load StdEnv/2020  julia/1.5.2
mpirun -np 2 julia hello.jl

Le code hello.jl est

using MPI
MPI.Init()
comm = MPI.COMM_WORLD
print("Hello world, I am rank $(MPI.Comm_rank(comm)) of $(MPI.Comm_size(comm))\n")
MPI.Barrier(comm)

Configurer le comportement des fils

Vous pouvez limiter le nombre de fils utilisés par Julia en configurant JULIA_NUM_THREADS=k; par exemple pour un processus unique pour une tâche de 12 CPU par tâche, k serait égal à 12. Il est habituel que le nombre de fils soit égal au nombre de processeurs; ceci est toutefois abordé dans la page Scalabilité. De plus, vous pouvez attacher des fils à un cœur en configurant JULIA_EXCLUSIVE à une valeur autre que zéro. Comme décrit dans la documentation, ceci enlève au système d'exploitation l'attribution des fils en les attachant aux cœurs en fonction de leur affinité. Dépendant des calculs effectués par les fils, ceci peut améliorer la performance quand il y a des informations précises sur les modes d'accès aux caches ou que le système d'exploitation attribue les fils de manière non voulue. La configuration de JULIA_EXCLUSIVE fonctionne uniquement si votre tâche a un accès exclusif aux nœuds et que tous les cœurs CPU ont été alloués à votre tâche. Puisque l'ordonnanceur Slurm attache les processus et les fils aux cœurs CPU, le fait de demander à Julia de réattacher les fils n'améliorera peut-être pas la performance.

La variable JULIA_THREAD_SLEEP_THRESHOLD contrôle le nombre de nanosecondes après lesquelles un fil qui est en rotation (spinning) est programmé pour dormir. Une valeur infinie exprimée par une chaîne de caractères indique que le fil en rotation de doit jamais dormir. Il peut être utile de modifier cette variable quand plusieurs fils se disputent fréquemment une ressource partagée; il serait alors peut-être préférable d'éliminer plus rapidement des fils en rotation. Dans un contexte de forte concurrence, les fils en rotation ne feraient qu'augmenter la charge sur les CPU. De la même manière, quand une ressource est rarement sollicitée, une basse latence peut se produire si on refuse aux fils de dormir, c'est-à-dire que le seuil soit défini comme étant infini.

Il va sans dire que ces valeurs ne devraient être configurées qu'après avoir profilé les problèmes potentiels de concurrence. Puisque Julia et particulièrement ses Base.Threads évoluent très rapidement, vous devriez toujours consulter la documentation pour vous assurer que le fait de modifier la configuration par défaut aura effectivement le résultat désiré.

Plus d'information

Webinaires produits par SHARCNET :