R/fr: Difference between revisions

From Alliance Doc
Jump to navigation Jump to search
No edit summary
(Created page with "Dans cet exemple, l'ordonnanceur pourrait placer les quatre processus sur un seul nœud. Ceci peut convenir, mais si vous voulez prouver que la même tâche peut être traitée si les processus sont placés sur des nœuds différents, ajoutez la ligne <code>#SBATCH --ntasks-per-node=2</code>.")
 
(287 intermediate revisions by 6 users not shown)
Line 4: Line 4:
R est un outil de calcul statistique et de graphiques. Il s'agit d'un langage de programmation additionné d'un environnement graphique, d'un débogueur, de l'accès à certaines fonctions de système et de la possibilité d'exécuter des scripts.
R est un outil de calcul statistique et de graphiques. Il s'agit d'un langage de programmation additionné d'un environnement graphique, d'un débogueur, de l'accès à certaines fonctions de système et de la possibilité d'exécuter des scripts.


Même si R n'a pas été développé pour le calcul de haute performance, sa popularité au sein de plusieurs disciplines scientifiques incluant l'ingénierie, les mathématiques, la statistique et la bioinformatique, en fait un outil essentiel sur les supercalculateurs dédiés à la recherche académique. Certaines fonctionnalités étant écrites en C, compilées et parallélisées par fils d'exécution, permettent d'atteindre des performances raisonnables sur un seul nœud de calcul. Grâce à la nature modulaire de R, les utilisateurs peuvent personnaliser leur configuration en installant des paquets dans leur répertoire personnel à partir du ''Comprehensive R Archive Network'' ([https://cran.r-project.org/ CRAN]).
Même si R n'a pas été développé pour le calcul de haute performance, sa popularité au sein de plusieurs disciplines scientifiques incluant le génie, les mathématiques, la statistique et la bio-informatique, en fait un outil essentiel sur les supercalculateurs dédiés à la recherche universitaire. Certaines fonctionnalités étant écrites en C, compilées et parallélisées par fils d'exécution, permettent d'atteindre des performances raisonnables sur un seul nœud de calcul. Grâce à la nature modulaire de R, les utilisateurs peuvent personnaliser leur configuration en installant des paquets dans leur répertoire personnel à partir du ''Comprehensive R Archive Network'' ([https://cran.r-project.org/ CRAN]).


== L'interpréteur R ==
Vous trouverez peut-être des informations utiles dans le billet de blogue de l'utilisatrice Julie Fortin intitulé [https://medium.com/the-nature-of-food/how-to-run-your-r-script-with-compute-canada-c325c0ab2973 <i>How to run your R script with Compute Canada</i>].
Chargez d'abord un module R. Comme plusieurs versions seront probablement disponibles, consultez la liste en lançant la commande   
 
{{Commande
== Interpréteur ==
Chargez d'abord un module R. Comme plusieurs versions sont disponibles, consultez la liste en lançant la commande   
{{Command
|module spider r
|module spider r
}}
}}
Pour charger un module R particulier, lancez la commande   
Pour charger un module R particulier, utilisez une variante de la commande   
{{Commande
{{Command
|module load r/3.3.3
|module load gcc/9.3.0 r/4.0.2
}}  
}}  
Lorsque R est chargé dans votre environnement, vous pouvez démarrer l'interpréteur R et exécuter du code R dans cet environnement :
Pour plus d'information, consultez [[Utiliser des modules|Utiliser des modules]].
 
Vous pouvez maintenant démarrer l'interpréteur et entrer le code R dans cet environnement.
{{Command
{{Command
|lang="R"
|lang="R"
|R
|R
|result=
|result=
R version 3.3.3 (2017-03-06) -- "Another Canoe"
R version 4.0.2 (2020-06-22) -- "Taking Off Again"
Copyright (C) 2017 The R Foundation for Statistical Computing
Copyright (C) 2020 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
Platform: x86_64-pc-linux-gnu (64-bit)


Line 39: Line 43:


> values <- c(3,5,7,9)
> values <- c(3,5,7,9)
> values[0]
> values[1]
[1] 3
[1] 3
> q()
> q()
}}
}}


Pour exécuter des scripts R, utilisez la commande <tt>Rscript</tt> suivie du fichier contenant les commandes R :
Pour exécuter des scripts R, utilisez la commande <code>Rscript</code> suivie du fichier contenant les commandes R&nbsp;:


{{Commande|Rscript computation.R}}
{{Command|Rscript computation.R}}


Cette commande passera automatiquement les options appropriées pour un traitement en lot, soit <tt>&#8209;&#8209;slave</tt> et <tt>&#8209;&#8209;no&#8209;restore</tt> à l'interpréteur R. Ces options empêcheront la création de fichiers d'espace de travail inutiles avec <tt>&#8209;&#8209;no&#8209;save</tt> lors d'un traitement en lot.
Cette commande passera automatiquement les options appropriées pour un traitement en lot, soit <code>--slave</code> et <code>--no-restore</code> à l'interpréteur R. Ces options empêcheront la création de fichiers d'espace de travail inutiles avec <code>--no-save</code> lors d'un traitement en lot.  


== Installer des paquets R ==
<b>Les calculs d'une durée de plus de deux ou trois minutes ne devraient pas être exécutés par un nœud de calcul, mais être soumis à l'ordonnanceur.</b>
Pour installer des paquets du répertoire [https://cran.r-project.org/ CRAN], vous pouvez utiliser la commande <tt>install.packages</tt> à l'intérieur de R. Par exemple, pour installer le paquet <tt>sp</tt> pour des données spatiales, exécutez sur le nœud frontal (''login node'') la commande


{{Commande
Voici un exemple de script simple :
{{File
  |name=job.sh
  |lang="bash"
  |contents=
#!/bin/bash
#SBATCH --account=def-someacct  # replace this with your own account
#SBATCH --mem-per-cpu=2000M      # memory; default unit is megabytes
#SBATCH --time=0-00:15          # time (DD-HH:MM)
module load gcc/9.3.0 r/4.0.2              # Adjust version and add the gcc module used for installing packages.
 
Rscript computation.R
}}
Pour plus d'information, consultez [[Running jobs/fr|Exécuter des tâches]].
 
== Installation des paquets R ==
 
=== install.packages() ===
 
Pour installer des paquets du [https://cran.r-project.org/ CRAN], vous pouvez utiliser <code>install.packages</code> dans une session R interactive sur un nœud de connexion. Puisque les nœuds de calcul de la plupart de nos grappes n'ont pas accès à l'internet, il n'est pas possible d'installer les paquets R dans une tâche en lots ou dans une tâche interactive. Parce que plusieurs paquets R sont développés avec la famille de compilateurs GNU, nous vous recommandons de [[Utiliser des modules|charger un module <code>gcc</code>]] avant de les installer et de toujours utilisez la même version du <code>gcc</code>.
{{Command
|module load gcc/9.3.0 r/4.0.2
}}
 
==== Installation pour une version particulière de R ====
Par exemple, pour installer le paquet <code>sp</code> qui offre des classes et des méthodes pour les données spatiales, utilisez cette commande sur un nœud de connexion.
 
{{Command
|R
|R
|result=
|result=
[...]
[...]
> install.packages("sp")
> install.packages('sp', repos='https://cloud.r-project.org/')
}}
 
Si l'argument <code>repos</code> n'est pas spécifié, on vous demandera de sélectionner un miroir pour le téléchargement. Idéalement, ce miroir sera géographiquement proche de la grappe que vous utilisez.
 
Avant l'installation, certains paquets requièrent la définition de la variable d'environnement <code>TMPDIR</code>.
 
==== Installation pour une ou plusieurs versions de R ====
Indiquez le répertoire local, selon le module de R qui est chargé.
{{Commands
|mkdir -p ~/.local/R/$EBVERSIONR/
|export R_LIBS{{=}}~/.local/R/$EBVERSIONR/
}}
Installez le paquet.
{{Command|R -e 'install.packages("sp", repos{{=}}"https://cloud.r-project.org/")'}}
 
Dans le script de soumission, vous devez ensuite charger le module R que vous voulez et configurer le répertoire local pour la bibliothèque avec  <code>export R_LIBS=~/.local/R/$EBVERSIONR/</code>.
 
=== Dépendances ===
Certains paquets utilisent des bibliothèques qui sont déjà installées sur nos grappes. Si la bibliothèque se trouve dans la liste des [[Available software/fr|logiciels disponibles]], chargez le [[Utiliser des modules/en|module]] approprié avant d'installer le paquet.
 
Par exemple, le paquet <code>rgdal</code> utilise la bibliothèque <code>gdal</code>. En lançant la commande <code>module spider gdal/2.2.1</code> nous voyons que les modules <code>nixpkgs</code> et <code>gcc</code> sont requis.  Si vous avez chargé <code>gcc</code> comme indiqué plus haut, ces deux modules devraient déjà être chargés. Vérifiez ceci avec la commande
 
{{Command
|module list
}}
 
Si l'installation d'un paquet échoue, portez attention au message d'erreur qui pourrait indiquer d'autres modules qui seraient requis. Pour plus d'information sur les commandes de <code>module</code>, consultez [[Utiliser des modules]].
 
===Téléchargement de paquets===
Si vous cherchez à installer un paquet que vous avez téléchargé, c'est-à-dire que vous n'avez pas utilisé <code>install.packages()</code>, vous pouvez l'installer comme suit. Par exemple, avec le paquet <tt>archive_package.tgz</tt>, vous exécuteriez la commande suivante dans l'interpréteur (<i>shell</i>)
 
{{Command
|R CMD INSTALL -l 'path for your local (home) R library' archive_package.tgz
}}
}}


Lorsque R vous le demandera, sélectionnez un miroir pour le téléchargement.  Idéalement, ce miroir sera géographiquement proche de la grappe que vous utilisez.
==Appels système==


Avant l'installation, certains paquets requièrent la définition de la variable d'environnement <tt>TMPDIR</tt>. D'autres paquets fonctionneront avec un compilateur Gnu C et/ou C++ plutôt qu'avec un compilateur de type Intel. Il est donc préférable de charger d'abord un des modules <tt>gcc</tt> avant de lancer R.
La commande R <code>system()</code> permet d'exécuter des commandes dans l'environnement actif, à l'intérieur de R; ceci risque de causer des problèmes sur nos grappes parce que R donne une valeur incorrecte à la variable d'environnement <code>LD_LIBRARY_PATH</code>. Utilisez plutôt la syntaxe <code>system("LD_LIBRARY_PATH=$RSNT_LD_LIBRARY_PATH <my system call>")</code> dans vos appels système.


Si vous cherchez à installer un paquet que vous avez téléchargé, supposons que l'archive se nomme <tt>paquet_archive.tgz</tt> (utile pour un paquet qui n'appartient pas au domaine CRAN), entrez la commande suivante dans votre interpréteur de commandes&nbsp;:
 
{{Commande
== Arguments passés à un script R ==
|R CMD INSTALL -l 'chemin pour votre bibliothèque locale (personnelle) R' paquet_archive.tgz
Il peut parfois être utile de passer des paramètres en argument à un script R pour éviter d'avoir à modifier le script pour plusieurs tâches semblables ou de devoir gérer plusieurs copies d'un même script. Ceci peut servir pour spécifier des paramètres numériques ou le nom des fichiers en entrée ou en sortie. Par exemple, au lieu d'employer une syntaxe comme
{{File
  |name=
  |lang="R"
  |contents=
filename = "input.csv"
iterations = 5
}}
}}
et de changer le code à chaque fois qu'un paramètre est modifié, les paramètres peuvent être passés au script au début avec
{{Command
|Rscript myscript.R  input_1.csv  5
}}
et par la suite
{{Command
|Rscript myscript.R  input_2.csv  10
}}
Dans le prochain exemple, il doit y avoir précisément deux arguments. Le premier devrait être une chaîne de caractères représentant le <b>nom</b> de la variable et le deuxième devrait <b>numéro</b> de la variable.
{{File
  |name=arguments_test.R
  |lang="R"
  |contents=
args = commandArgs(trailingOnly=TRUE)
# test if there is at least two arguments: if not, return an error
if (length(args)<2) {
  stop("At least two arguments must be supplied ('name' (text) and 'numer' (integer) )", call.=FALSE)
}
name      <- args[1]                # read first argument as string
number    <- as.integer( args[2] )  # read second argument as integer
print(paste("Processing with name:'", name, "' and number:'", number,"'", sep = ''))
}}
Ce script peut être utilisé comme suit
{{Command
|lang="R"
| Rscript arguments_test.R  Hello  42
|result=
[1] "Processing with name:'Hello' and number:'42'"
}}
==Parallélisation==
Si les processeurs de nos grappes sont on ne peut plus ordinaires, ce qui rend ces <i>supercalculateurs</i> intéressants, c'est qu'ils offrent des milliers de CPU sur un réseau très performant. Pour profiter de cet avantage, vous devez utiliser la programmation parallèle. Cependant, avant d'allouer beaucoup de temps et d'effort à paralléliser votre code R, assurez-vous que votre implémentation séquentielle est aussi efficiente que possible. Comme dans tout langage interprété, d'importants goulots d'étranglement (<i>bottlenecks</i>) sont causés par les boucles et particulièrement les boucles imbriquées, ce qui a un impact sur la performance. Lorsque possible, essayez d'utiliser les fonctions vectorielles et les autres éléments plus fonctionnels comme la famille des fonctions <code>apply</code> et la fonction <code>ifelse</code>. Vous obtiendrez souvent un gain de performance en éliminant une boucle plutôt que de paralléliser son exécution avec plusieurs cœurs CPU.
La page [https://cran.r-project.org/web/views/HighPerformanceComputing.html CRAN Task View on High-Performance and Parallel Computing with R]
mentionne un grand nombre de paquets pouvant être utilisés avec R pour la programmation parallèle.
Vous trouverez une excellente vue d'ensemble et des conseils dans le contenu du
[https://education.scinet.utoronto.ca/course/view.php?id=1333 colloque de Compute Ontario du 11 octobre 2023 intitulé <i>High-Performance Computing in R</i>]
([https://education.scinet.utoronto.ca/mod/resource/view.php?id=2887 diapositives]).
Vous trouverez d'autres renseignements et exemples dans les sous-sections ci-dessous.
<b>Terminologie</b> : Dans notre documentation, les termes <i>nœud</i> et <i>hôte</i> sont quelquefois employés pour désigner un ordinateur distinct; un regroupement de <i>nœuds</i> ou d'<i>hôtes</i> constitue une <i>grappe</i>.
<i>nœud</i> désigne souvent un <i>processus de travail</i> (worker process); un regroupement de ces processus constitue une <i>grappe</i>. Prenons comme exemple la citation suivante : «&nbsp;Following <b>snow</b>, a pool of worker processes listening <i>via</i> sockets for commands from the master is called a 'cluster' of nodes.&nbsp;»<ref>https://stat.ethz.ch/R-manual/R-devel/library/parallel/doc/parallel.pdf</ref><br/>.
=== doParallel et foreach ===
====Utilisation====
Foreach peut être vu comme une interface unifiée pour tous les systèmes dorsaux (<i>backends</i>) comme doMC, doMPI, doParallel, doRedis, etc. et fonctionne sur toutes les plateformes pourvu que le système dorsal soit fonctionnel. doParallel agit comme interface entre foreach et le paquet parallèle et peut être chargé seul. Certains [[Scalability/fr|problèmes de performance connus]] surviennent avec foreach lors de l'exécution d'un très grand nombre de très petites tâches. Notez que l'exemple simple qui suit n'utilise pas l'appel foreach() de façon optimale.
Enregistrez le système dorsal en lui indiquant le nombre de cœurs disponibles. Si le système dorsal n'est pas enregistré, foreach assume que le nombre de cœurs est 1 et exécute les itérations de façon séquentielle.
La méthode générale pour utiliser foreach est :
# chargez foreach et le paquet dorsal;
# enregistrez le paquet dorsal;
# appelez foreach() en le laissant sur la même ligne que l'opérateur %do% (série) ou %dopar%.
====Exécution====
1. Placez le code R dans un fichier script, ici le fichier <i>test_foreach.R</i>.
{{File
  |name=test_foreach.R
  |lang="r"
  |contents=
# library(foreach) # optionnel si doParallel est utilisé
library(doParallel) #
# a very simple function
test_func <- function(var1, var2) {
    # some heavy workload
    sum <- 0
    for (i in c(1:3141593)) {
        sum <- sum + var1 * sin(var2 / i)
    }
    return(sqrt(sum))
}
# nous allons itérer selon deux ensembles de valeurs que vous pouvez modifier pour tester le fonctionnement de foreach
var1.v = c(1:8)
var2.v = seq(0.1, 1, length.out = 8)
# La variable d'environnement SLURM_CPUS_PER_TASK contient le nombre de coeurs par tâche.
# Elle est définie par SLURM.
# Évitez de fixer un nombre de coeurs manuellement dans le code source.
ncores = Sys.getenv("SLURM_CPUS_PER_TASK")
registerDoParallel(cores=ncores) # Demande ncores "Parallel Workers"
print(ncores) # Affiche le nombre de coeurs disponibles et demandé
getDoParWorkers() # Affiche le nombre de "Parallel Workers" actuel
# attention! foreach() et %dopar% doivent être sur la même ligne de code!
foreach(var1=var1.v, .combine=rbind) %:% foreach(var2=var2.v, .combine=rbind) %dopar% {test_func(var1=var1, var2=var2)}
}}
Copiez ce qui suit dans le script <i>job_foreach.sh</i>.
{{File
  |name=job_foreach.sh
  |lang="bash"
  |contents=
#!/bin/bash
#SBATCH --account=def-someacct  # replace this with your supervisors account
#SBATCH --nodes=1                # number of node MUST be 1
#SBATCH --cpus-per-task=4        # number of processes
#SBATCH --mem-per-cpu=2048M      # memory; default unit is megabytes
#SBATCH --time=0-00:15          # time (DD-HH:MM)
#SBATCH --mail-user=yourname@someplace.com # Send email updates to you or someone else
#SBATCH --mail-type=ALL          # send an email in all cases (job started, job ended, job aborted)
module load gcc/9.3.0 r/4.0.2
export R_LIBS=~/local/R_libs/
R CMD BATCH --no-save --no-restore test_foreach.R
}}
3. Soumettez la tâche.
{{Command|sbatch job_foreach.sh}}
Pour plus d'information sur comment soumettre des tâches, consultez [[Running jobs/fr|Exécuter des tâches]].
=== doParallel et makeCluster ===
====Utilisation====
Il faut enregistrer le système dorsal (<i>backend</i>) en lui donnant le nom des nœuds, multiplié par le nombre voulu de processus. Par exemple, nous créerions une grappe composée des hôtes <code>node1 node1 node2 node2</code>. Le type de grappe <i>PSOCK</i> exécute des commandes par des connexions SSH vers les nœuds.
====Exécution====
1. Placer le code R dans un fichier script, ici <code>test_makecluster.R</code>.
{{File
  |name=test_makecluster.R
  |lang="r"
  |contents=
library(doParallel)
# Create an array from the NODESLIST environnement variable
nodeslist = unlist(strsplit(Sys.getenv("NODESLIST"), split=" "))
# Create the cluster with the nodes name. One process per count of node name.
# nodeslist = node1 node1 node2 node2, means we are starting 2 processes on node1, likewise on node2.
cl = makeCluster(nodeslist, type = "PSOCK")
registerDoParallel(cl)
# Compute (Source : https://cran.r-project.org/web/packages/doParallel/vignettes/gettingstartedParallel.pdf)
x <- iris[which(iris[,5] != "setosa"), c(1,5)]
trials <- 10000
foreach(icount(trials), .combine=cbind) %dopar%
    {
    ind <- sample(100, 100, replace=TRUE)
    result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit))
    coefficients(result1)
    }
# Don't forget to release resources
stopCluster(cl)
}}
2. Copiez les lignes suivantes dans un script pour soumettre la tâche, ici <code>job_makecluster.sh</code>.
{{File
  |name=job_makecluster.sh
  |lang="bash"
  |escape|mode=text
  |contents=
#!/bin/bash
#SBATCH --account=def-someacct  # à remplacer par un compte approprié
#SBATCH --ntasks=4              # nombre de processus
#SBATCH --mem-per-cpu=512M      # mémoire par coeur CPU; valeur en Mo par défaut
#SBATCH --time=00:05:00        # temps (HH:MM:SS)
module load gcc/9.3.0 r/4.0.2
# Export the nodes names.
# If all processes are allocated on the same node, NODESLIST contains : node1 node1 node1 node1
# Cut the domain name and keep only the node name
export NODESLIST=$(echo $(srun hostname {{!}} cut -f 1 -d '.'))
R -f test_makecluster.R
}}
Dans cet exemple, l'ordonnanceur pourrait placer les quatre processus sur un seul nœud.
Ceci peut convenir, mais si vous voulez prouver que la même tâche peut être traitée si les processus
sont placés sur des nœuds différents, ajoutez la ligne <code>#SBATCH --ntasks-per-node=2</code>.
3. Soumettez la tâche avec
{{Command|sbatch job_makecluster.sh}}
Pour plus d'information sur comment soumettre une tâche, voyez [[Running jobs/fr|Exécuter des tâches]].


=== Rmpi ===
=== Rmpi ===
Les directives qui suivent ne fonctionnent pas sur [[Cedar/fr|Cedar]]; utilisez plutôt une autre grappe.


====Installation====
====Installation====
La procédure suivante installe [https://cran.r-project.org/web/packages/Rmpi/index.html Rmpi], une interface (''wrapper'') pour les routines MPI qui permet d'exécuter R en parallèle.
La procédure suivante installe [https://cran.r-project.org/web/packages/Rmpi/index.html Rmpi], une interface (<i>wrapper</i>) pour les routines MPI qui permet d'exécuter R en parallèle.


1. Voyez les modules R disponibles avec la commande
1. Voyez les modules R disponibles avec la commande
  <source lang="bash">
  <source lang="bash">
module spider r
module spider r
</source>
</source>


2. Sélectionnez la dernière version (novembre 2017) et chargez le module OpenMPI approprié.
2. Sélectionnez la version et chargez le module OpenMPI approprié. Dans notre exemple, la version 4.0.3 est utilisée, pour que les processus s'exécutent correctement.
  <source lang="bash">
  <source lang="bash">
module load r/3.4.0
module load gcc/11.3.0
module load openmpi/1.10.7
module load r/4.2.1
</sourc
module load openmpi/4.1.4
</source>


<div class="mw-translate-fuzzy">
3. Téléchargez [https://cran.r-project.org/web/packages/Rmpi/index.html la dernière version de Rmpi] en remplaçant le numéro de la version selon le cas.
3. Téléchargez [https://cran.r-project.org/web/packages/Rmpi/index.html la dernière version de R] en remplaçant le numéro de la version selon le cas.
<source lang="bash">
</div>
wget https://cran.r-project.org/src/contrib/Rmpi_0.6-9.2.tar.gz
</source>


<div class="mw-translate-fuzzy">
4. Indiquez le répertoire dans lequel vous voulez copier les fichiers; vous devez avoir une permission d'écriture pour ce répertoire. Le nom du répertoire peut être modifié.
4. Indiquez le répertoire dans lequel vous voulez copier les fichiers; vous devez avoir une permission d'écriture pour ce répertoire. Le nom du répertoire peut être modifié.
</div>
<source lang="bash">
mkdir -p ~/local/R_libs/
export R_LIBS=~/local/R_libs/
</source>


<div class="mw-translate-fuzzy">
5. Lancez la commande d'installation.
5. Lancez la commande d'installation.
</div>
<source lang="bash">
R CMD INSTALL --configure-args="--with-Rmpi-include=$EBROOTOPENMPI/include  --with-Rmpi-libpath=$EBROOTOPENMPI/lib --with-Rmpi-type='OPENMPI' " Rmpi_0.6-9.2.tar.gz
</source>


Cette procédure utilise la version 1.10.7 qui est nécessaire pour bien démarrer les processus. Notez que le module par défaut (2.1.1) ne fonctionne pas bien présentement.
Portez attention au message d'erreur qui s'affiche quand l'installation d'un paquet échoue; il pourrait indiquer d'autres modules qui seraient nécessaires.


====Exécution====
====Exécution====


<div class="mw-translate-fuzzy">
1. Placez le code R dans un fichier script, ici le fichier <i>test.R</i>.
1. Placez le code R dans un fichier script, ici le fichier ''test.R''.
 
</div>
{{File
  |name=test.R
  |lang="r"
  |contents=
#Tell all slaves to return a message identifying themselves.
library("Rmpi")
sprintf("TEST mpi.universe.size() =  %i", mpi.universe.size())
ns <- mpi.universe.size() - 1
sprintf("TEST attempt to spawn %i slaves", ns)
mpi.spawn.Rslaves(nslaves=ns)
mpi.remote.exec(paste("I am",mpi.comm.rank(),"of",mpi.comm.size()))
mpi.remote.exec(paste(mpi.comm.get.parent()))
#Send execution commands to the slaves
x<-5
#These would all be pretty correlated one would think
x<-mpi.remote.exec(rnorm,x)
length(x)
x
mpi.close.Rslaves()
mpi.quit()
}}


<source lang="r">
2. Copiez ce qui suit dans le script <i>job.sh</i>.
#Tell all slaves to return a message identifying themselves.
  library("Rmpi")
  sprintf("TEST mpi.universe.size() =  %i", mpi.universe.size())
  ns <- mpi.universe.size() - 1
  sprintf("TEST attempt to spawn %i slaves", ns)
  mpi.spawn.Rslaves(nslaves=ns)
  mpi.remote.exec(paste("I am",mpi.comm.rank(),"of",mpi.comm.size()))
  mpi.remote.exec(paste(mpi.comm.get.parent()))
  #Send execution commands to the slaves
  x<-5
  #These would all be pretty correlated one would think
  x<-mpi.remote.exec(rnorm,x)
  length(x)
  x
</source>


<div class="mw-translate-fuzzy">
{{File
2. Copiez ce qui suit dans le script job.sh.
  |name=job.sh
</div>
  |lang="bash"
  |contents=
#!/bin/bash
#SBATCH --account=def-someacct  # à remplacer par un compte approprié
#SBATCH --ntasks=5              # nombre de processus MPI
#SBATCH --mem-per-cpu=2048M      # mémoire par coeur CPU; valeur en Mo par défaut
#SBATCH --time=0-00:15          # temps (JJ-HH:MM)


<source lang="bash">
module load gcc/11.3.0
#!/bin/bash
module load r/4.2.1
#SBATCH --account=def-ppomorsk # replace this with your own account
module load openmpi/4.1.4
#SBATCH --ntasks=5              # number of MPI processes
export R_LIBS=~/local/R_libs/
#SBATCH --mem-per-cpu=2048M      # memory; default unit is megabytes
mpirun -np 1 R CMD BATCH test.R test.txt
#SBATCH --time=0-00:15          # time (DD-HH:MM)
}}
module load r/3.4.0
module load openmpi/1.10.7
export R_LIBS=~/local/R_libs/
mpirun -np 1 R CMD BATCH test.R test.txt
</source>


<div class="mw-translate-fuzzy">
3. Soumettez la tâche.
3. Soumettez la tâche.
</div>


  <source lang="bash">
  <source lang="bash">
sbatch job.sh
sbatch job.sh
</source>
</source>


Pour plus d'information sur comment soumettre des tâches, consultez [[Running jobs/fr|Exécuter des tâches]].
Pour plus d'information sur comment soumettre des tâches, consultez [[Running jobs/fr|Exécuter des tâches]].

Latest revision as of 14:34, 10 June 2024

Other languages:

R est un outil de calcul statistique et de graphiques. Il s'agit d'un langage de programmation additionné d'un environnement graphique, d'un débogueur, de l'accès à certaines fonctions de système et de la possibilité d'exécuter des scripts.

Même si R n'a pas été développé pour le calcul de haute performance, sa popularité au sein de plusieurs disciplines scientifiques incluant le génie, les mathématiques, la statistique et la bio-informatique, en fait un outil essentiel sur les supercalculateurs dédiés à la recherche universitaire. Certaines fonctionnalités étant écrites en C, compilées et parallélisées par fils d'exécution, permettent d'atteindre des performances raisonnables sur un seul nœud de calcul. Grâce à la nature modulaire de R, les utilisateurs peuvent personnaliser leur configuration en installant des paquets dans leur répertoire personnel à partir du Comprehensive R Archive Network (CRAN).

Vous trouverez peut-être des informations utiles dans le billet de blogue de l'utilisatrice Julie Fortin intitulé How to run your R script with Compute Canada.

Interpréteur

Chargez d'abord un module R. Comme plusieurs versions sont disponibles, consultez la liste en lançant la commande

Question.png
[name@server ~]$ module spider r

Pour charger un module R particulier, utilisez une variante de la commande

Question.png
[name@server ~]$ module load gcc/9.3.0 r/4.0.2

Pour plus d'information, consultez Utiliser des modules.

Vous pouvez maintenant démarrer l'interpréteur et entrer le code R dans cet environnement.

Question.png
[name@server ~]$ R
R version 4.0.2 (2020-06-22) -- "Taking Off Again"
Copyright (C) 2020 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> values <- c(3,5,7,9)
> values[1]
[1] 3
> q()

Pour exécuter des scripts R, utilisez la commande Rscript suivie du fichier contenant les commandes R :

Question.png
[name@server ~]$ Rscript computation.R

Cette commande passera automatiquement les options appropriées pour un traitement en lot, soit --slave et --no-restore à l'interpréteur R. Ces options empêcheront la création de fichiers d'espace de travail inutiles avec --no-save lors d'un traitement en lot.

Les calculs d'une durée de plus de deux ou trois minutes ne devraient pas être exécutés par un nœud de calcul, mais être soumis à l'ordonnanceur.

Voici un exemple de script simple :

File : job.sh

#!/bin/bash
#SBATCH --account=def-someacct   # replace this with your own account
#SBATCH --mem-per-cpu=2000M      # memory; default unit is megabytes
#SBATCH --time=0-00:15           # time (DD-HH:MM)
module load gcc/9.3.0 r/4.0.2              # Adjust version and add the gcc module used for installing packages.

Rscript computation.R


Pour plus d'information, consultez Exécuter des tâches.

Installation des paquets R

install.packages()

Pour installer des paquets du CRAN, vous pouvez utiliser install.packages dans une session R interactive sur un nœud de connexion. Puisque les nœuds de calcul de la plupart de nos grappes n'ont pas accès à l'internet, il n'est pas possible d'installer les paquets R dans une tâche en lots ou dans une tâche interactive. Parce que plusieurs paquets R sont développés avec la famille de compilateurs GNU, nous vous recommandons de charger un module gcc avant de les installer et de toujours utilisez la même version du gcc.

Question.png
[name@server ~]$ module load gcc/9.3.0 r/4.0.2

Installation pour une version particulière de R

Par exemple, pour installer le paquet sp qui offre des classes et des méthodes pour les données spatiales, utilisez cette commande sur un nœud de connexion.

Question.png
[name@server ~]$ R
[...]
> install.packages('sp', repos='https://cloud.r-project.org/')

Si l'argument repos n'est pas spécifié, on vous demandera de sélectionner un miroir pour le téléchargement. Idéalement, ce miroir sera géographiquement proche de la grappe que vous utilisez.

Avant l'installation, certains paquets requièrent la définition de la variable d'environnement TMPDIR.

Installation pour une ou plusieurs versions de R

Indiquez le répertoire local, selon le module de R qui est chargé.

[name@server ~]$ mkdir -p ~/.local/R/$EBVERSIONR/
[name@server ~]$ export R_LIBS=~/.local/R/$EBVERSIONR/

Installez le paquet.

Question.png
[name@server ~]$ R -e 'install.packages("sp", repos="https://cloud.r-project.org/")'

Dans le script de soumission, vous devez ensuite charger le module R que vous voulez et configurer le répertoire local pour la bibliothèque avec export R_LIBS=~/.local/R/$EBVERSIONR/.

Dépendances

Certains paquets utilisent des bibliothèques qui sont déjà installées sur nos grappes. Si la bibliothèque se trouve dans la liste des logiciels disponibles, chargez le module approprié avant d'installer le paquet.

Par exemple, le paquet rgdal utilise la bibliothèque gdal. En lançant la commande module spider gdal/2.2.1 nous voyons que les modules nixpkgs et gcc sont requis. Si vous avez chargé gcc comme indiqué plus haut, ces deux modules devraient déjà être chargés. Vérifiez ceci avec la commande

Question.png
[name@server ~]$ module list

Si l'installation d'un paquet échoue, portez attention au message d'erreur qui pourrait indiquer d'autres modules qui seraient requis. Pour plus d'information sur les commandes de module, consultez Utiliser des modules.

Téléchargement de paquets

Si vous cherchez à installer un paquet que vous avez téléchargé, c'est-à-dire que vous n'avez pas utilisé install.packages(), vous pouvez l'installer comme suit. Par exemple, avec le paquet archive_package.tgz, vous exécuteriez la commande suivante dans l'interpréteur (shell)

Question.png
[name@server ~]$ R CMD INSTALL -l 'path for your local (home) R library' archive_package.tgz

Appels système

La commande R system() permet d'exécuter des commandes dans l'environnement actif, à l'intérieur de R; ceci risque de causer des problèmes sur nos grappes parce que R donne une valeur incorrecte à la variable d'environnement LD_LIBRARY_PATH. Utilisez plutôt la syntaxe system("LD_LIBRARY_PATH=$RSNT_LD_LIBRARY_PATH <my system call>") dans vos appels système.


Arguments passés à un script R

Il peut parfois être utile de passer des paramètres en argument à un script R pour éviter d'avoir à modifier le script pour plusieurs tâches semblables ou de devoir gérer plusieurs copies d'un même script. Ceci peut servir pour spécifier des paramètres numériques ou le nom des fichiers en entrée ou en sortie. Par exemple, au lieu d'employer une syntaxe comme

File :

filename = "input.csv"
iterations = 5


et de changer le code à chaque fois qu'un paramètre est modifié, les paramètres peuvent être passés au script au début avec

Question.png
[name@server ~]$ Rscript myscript.R  input_1.csv  5

et par la suite

Question.png
[name@server ~]$ Rscript myscript.R  input_2.csv  10

Dans le prochain exemple, il doit y avoir précisément deux arguments. Le premier devrait être une chaîne de caractères représentant le nom de la variable et le deuxième devrait numéro de la variable.

File : arguments_test.R

args = commandArgs(trailingOnly=TRUE)

# test if there is at least two arguments: if not, return an error
if (length(args)<2) {
  stop("At least two arguments must be supplied ('name' (text) and 'numer' (integer) )", call.=FALSE)
}

name      <- args[1]                # read first argument as string
number    <- as.integer( args[2] )  # read second argument as integer

print(paste("Processing with name:'", name, "' and number:'", number,"'", sep = ''))


Ce script peut être utilisé comme suit

Question.png
[name@server ~]$  Rscript arguments_test.R  Hello  42
[1] "Processing with name:'Hello' and number:'42'"

Parallélisation

Si les processeurs de nos grappes sont on ne peut plus ordinaires, ce qui rend ces supercalculateurs intéressants, c'est qu'ils offrent des milliers de CPU sur un réseau très performant. Pour profiter de cet avantage, vous devez utiliser la programmation parallèle. Cependant, avant d'allouer beaucoup de temps et d'effort à paralléliser votre code R, assurez-vous que votre implémentation séquentielle est aussi efficiente que possible. Comme dans tout langage interprété, d'importants goulots d'étranglement (bottlenecks) sont causés par les boucles et particulièrement les boucles imbriquées, ce qui a un impact sur la performance. Lorsque possible, essayez d'utiliser les fonctions vectorielles et les autres éléments plus fonctionnels comme la famille des fonctions apply et la fonction ifelse. Vous obtiendrez souvent un gain de performance en éliminant une boucle plutôt que de paralléliser son exécution avec plusieurs cœurs CPU.

La page CRAN Task View on High-Performance and Parallel Computing with R mentionne un grand nombre de paquets pouvant être utilisés avec R pour la programmation parallèle. Vous trouverez une excellente vue d'ensemble et des conseils dans le contenu du colloque de Compute Ontario du 11 octobre 2023 intitulé High-Performance Computing in R (diapositives).

Vous trouverez d'autres renseignements et exemples dans les sous-sections ci-dessous.

Terminologie : Dans notre documentation, les termes nœud et hôte sont quelquefois employés pour désigner un ordinateur distinct; un regroupement de nœuds ou d'hôtes constitue une grappe. nœud désigne souvent un processus de travail (worker process); un regroupement de ces processus constitue une grappe. Prenons comme exemple la citation suivante : « Following snow, a pool of worker processes listening via sockets for commands from the master is called a 'cluster' of nodes. »[1]
.

doParallel et foreach

Utilisation

Foreach peut être vu comme une interface unifiée pour tous les systèmes dorsaux (backends) comme doMC, doMPI, doParallel, doRedis, etc. et fonctionne sur toutes les plateformes pourvu que le système dorsal soit fonctionnel. doParallel agit comme interface entre foreach et le paquet parallèle et peut être chargé seul. Certains problèmes de performance connus surviennent avec foreach lors de l'exécution d'un très grand nombre de très petites tâches. Notez que l'exemple simple qui suit n'utilise pas l'appel foreach() de façon optimale.

Enregistrez le système dorsal en lui indiquant le nombre de cœurs disponibles. Si le système dorsal n'est pas enregistré, foreach assume que le nombre de cœurs est 1 et exécute les itérations de façon séquentielle.

La méthode générale pour utiliser foreach est :

  1. chargez foreach et le paquet dorsal;
  2. enregistrez le paquet dorsal;
  3. appelez foreach() en le laissant sur la même ligne que l'opérateur %do% (série) ou %dopar%.

Exécution

1. Placez le code R dans un fichier script, ici le fichier test_foreach.R.


File : test_foreach.R

# library(foreach) # optionnel si doParallel est utilisé
library(doParallel) #

# a very simple function
test_func <- function(var1, var2) {
    # some heavy workload
    sum <- 0
    for (i in c(1:3141593)) {
        sum <- sum + var1 * sin(var2 / i)
    }
    return(sqrt(sum))
}

# nous allons itérer selon deux ensembles de valeurs que vous pouvez modifier pour tester le fonctionnement de foreach
var1.v = c(1:8)
var2.v = seq(0.1, 1, length.out = 8)

# La variable d'environnement SLURM_CPUS_PER_TASK contient le nombre de coeurs par tâche.
# Elle est définie par SLURM.
# Évitez de fixer un nombre de coeurs manuellement dans le code source.
ncores = Sys.getenv("SLURM_CPUS_PER_TASK") 

registerDoParallel(cores=ncores) # Demande ncores "Parallel Workers"
print(ncores) # Affiche le nombre de coeurs disponibles et demandé
getDoParWorkers() # Affiche le nombre de "Parallel Workers" actuel

# attention! foreach() et %dopar% doivent être sur la même ligne de code!
foreach(var1=var1.v, .combine=rbind) %:% foreach(var2=var2.v, .combine=rbind) %dopar% {test_func(var1=var1, var2=var2)}



Copiez ce qui suit dans le script job_foreach.sh.


File : job_foreach.sh

#!/bin/bash
#SBATCH --account=def-someacct   # replace this with your supervisors account
#SBATCH --nodes=1                # number of node MUST be 1
#SBATCH --cpus-per-task=4        # number of processes
#SBATCH --mem-per-cpu=2048M      # memory; default unit is megabytes
#SBATCH --time=0-00:15           # time (DD-HH:MM)
#SBATCH --mail-user=yourname@someplace.com # Send email updates to you or someone else
#SBATCH --mail-type=ALL          # send an email in all cases (job started, job ended, job aborted)

module load gcc/9.3.0 r/4.0.2
export R_LIBS=~/local/R_libs/
R CMD BATCH --no-save --no-restore test_foreach.R


3. Soumettez la tâche.

Question.png
[name@server ~]$ sbatch job_foreach.sh

Pour plus d'information sur comment soumettre des tâches, consultez Exécuter des tâches.

doParallel et makeCluster

Utilisation

Il faut enregistrer le système dorsal (backend) en lui donnant le nom des nœuds, multiplié par le nombre voulu de processus. Par exemple, nous créerions une grappe composée des hôtes node1 node1 node2 node2. Le type de grappe PSOCK exécute des commandes par des connexions SSH vers les nœuds.

Exécution

1. Placer le code R dans un fichier script, ici test_makecluster.R.

File : test_makecluster.R

library(doParallel)

# Create an array from the NODESLIST environnement variable
nodeslist = unlist(strsplit(Sys.getenv("NODESLIST"), split=" "))

# Create the cluster with the nodes name. One process per count of node name.
# nodeslist = node1 node1 node2 node2, means we are starting 2 processes on node1, likewise on node2.
cl = makeCluster(nodeslist, type = "PSOCK") 
registerDoParallel(cl)

# Compute (Source : https://cran.r-project.org/web/packages/doParallel/vignettes/gettingstartedParallel.pdf)
x <- iris[which(iris[,5] != "setosa"), c(1,5)]
trials <- 10000

foreach(icount(trials), .combine=cbind) %dopar%
    {
    ind <- sample(100, 100, replace=TRUE)
    result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit))
    coefficients(result1)
    }

# Don't forget to release resources
stopCluster(cl)


2. Copiez les lignes suivantes dans un script pour soumettre la tâche, ici job_makecluster.sh.

File : job_makecluster.sh

#!/bin/bash
#SBATCH --account=def-someacct  # à remplacer par un compte approprié
#SBATCH --ntasks=4              # nombre de processus
#SBATCH --mem-per-cpu=512M      # mémoire par coeur CPU; valeur en Mo par défaut
#SBATCH --time=00:05:00         # temps (HH:MM:SS)

module load gcc/9.3.0 r/4.0.2

# Export the nodes names. 
# If all processes are allocated on the same node, NODESLIST contains : node1 node1 node1 node1
# Cut the domain name and keep only the node name
export NODESLIST=$(echo $(srun hostname | cut -f 1 -d '.'))
R -f test_makecluster.R


Dans cet exemple, l'ordonnanceur pourrait placer les quatre processus sur un seul nœud. Ceci peut convenir, mais si vous voulez prouver que la même tâche peut être traitée si les processus sont placés sur des nœuds différents, ajoutez la ligne #SBATCH --ntasks-per-node=2.

3. Soumettez la tâche avec

Question.png
[name@server ~]$ sbatch job_makecluster.sh

Pour plus d'information sur comment soumettre une tâche, voyez Exécuter des tâches.

Rmpi

Les directives qui suivent ne fonctionnent pas sur Cedar; utilisez plutôt une autre grappe.

Installation

La procédure suivante installe Rmpi, une interface (wrapper) pour les routines MPI qui permet d'exécuter R en parallèle.

1. Voyez les modules R disponibles avec la commande

module spider r

2. Sélectionnez la version et chargez le module OpenMPI approprié. Dans notre exemple, la version 4.0.3 est utilisée, pour que les processus s'exécutent correctement.

module load gcc/11.3.0
module load r/4.2.1
module load openmpi/4.1.4

3. Téléchargez la dernière version de Rmpi en remplaçant le numéro de la version selon le cas.

wget https://cran.r-project.org/src/contrib/Rmpi_0.6-9.2.tar.gz

4. Indiquez le répertoire dans lequel vous voulez copier les fichiers; vous devez avoir une permission d'écriture pour ce répertoire. Le nom du répertoire peut être modifié.

mkdir -p ~/local/R_libs/
export R_LIBS=~/local/R_libs/

5. Lancez la commande d'installation.

R CMD INSTALL --configure-args="--with-Rmpi-include=$EBROOTOPENMPI/include   --with-Rmpi-libpath=$EBROOTOPENMPI/lib --with-Rmpi-type='OPENMPI' " Rmpi_0.6-9.2.tar.gz

Portez attention au message d'erreur qui s'affiche quand l'installation d'un paquet échoue; il pourrait indiquer d'autres modules qui seraient nécessaires.

Exécution

1. Placez le code R dans un fichier script, ici le fichier test.R.


File : test.R

#Tell all slaves to return a message identifying themselves.
library("Rmpi")
sprintf("TEST mpi.universe.size() =  %i", mpi.universe.size())
ns <- mpi.universe.size() - 1
sprintf("TEST attempt to spawn %i slaves", ns)
mpi.spawn.Rslaves(nslaves=ns)
mpi.remote.exec(paste("I am",mpi.comm.rank(),"of",mpi.comm.size()))
mpi.remote.exec(paste(mpi.comm.get.parent()))
#Send execution commands to the slaves
x<-5
#These would all be pretty correlated one would think
x<-mpi.remote.exec(rnorm,x)
length(x)
x
mpi.close.Rslaves()
mpi.quit()


2. Copiez ce qui suit dans le script job.sh.


File : job.sh

#!/bin/bash
#SBATCH --account=def-someacct   # à remplacer par un compte approprié
#SBATCH --ntasks=5               # nombre de processus MPI
#SBATCH --mem-per-cpu=2048M      # mémoire par coeur CPU; valeur en Mo par défaut
#SBATCH --time=0-00:15           # temps (JJ-HH:MM)

module load gcc/11.3.0
module load r/4.2.1
module load openmpi/4.1.4
export R_LIBS=~/local/R_libs/
mpirun -np 1 R CMD BATCH test.R test.txt


3. Soumettez la tâche.

sbatch job.sh

Pour plus d'information sur comment soumettre des tâches, consultez Exécuter des tâches.