Différences entre les versions de « Snow »

 
(99 versions intermédiaires par 4 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
Les calculs statistiques qui exigent des longues procédures, peuvent être accéléré de manière significative en utilisant plusieurs ordinateurs en parallèle.
+
Le package '''snow''' (Simple Network Of Workstations) offre la possibilité d'exécuter du code [[R|R]] en parallèle sur plusieurs ordinateurs. Les calculs statistiques lourds peuvent ainsi être accélérés de manière significative.
 +
__TOC__
  
Le package «snow» (Simple Network Of Workstations) nous donne la possibilité d'avoir un cluster de postes de travail pour les calculs parallèles dans R.
+
=== Configuration ===
 +
{|
 +
|rowspan="1"|[[Image:Attention.png|40px]]
 +
|
 +
Avant d'entreprendre des simulations numériques sur les machines du Département, vous êtes encouragé à consulter la page [[Simulations|Simulations]], en particulier la section traitant des [[Simulations#Consignes_à_respecter|consignes à respecter]].
 +
|}
  
==Lancer le script Snow au DMS==
+
Ci-dessous, vous trouverez les instructions pour la configuration initiale de <tt>snow</tt> pour votre compte au DMS.
* Ouvrez un terminal
 
* Taper la commande suivante: makeKey.sh (ceci est pour empêcher le SSH de vous demander votre mot de passe à chaque fois que vous tentez de vous connecter à d'autres machines du réseau)
 
  
* Répondez par «y» à la question suivante&nbsp;:
+
* Tapez
 +
<pre>
 +
makeKey.sh
 +
</pre>
 +
dans un terminal de [[Commandes_élémentaires|commandes]]. La ligne précédente exécute le script <tt>makeKey.sh</tt> qui configure le système [[La_commande_ssh|<tt>ssh</tt>]] afin que vous puissiez vous brancher à d'autres machines du Département sans qu'on vous demande votre mot de passe.
  
 +
* Si la question
 
<pre>
 
<pre>
 
Generating public/private rsa key pair.
 
Generating public/private rsa key pair.
Ligne 14 : Ligne 23 :
 
Overwrite (y/n)?  
 
Overwrite (y/n)?  
 
</pre>
 
</pre>
 +
apparaît, répondez par &laquo;&nbsp;y&nbsp;&raquo;.
 +
 +
* Appuyez sur <tt>Entrée</tt> (''Enter'') lorsque vous apercevez les messages
 +
<pre>
 +
Enter passphrase (empty for no passphrase):
 +
</pre>
 +
et
 +
<pre>
 +
Enter same passphrase again:
 +
</pre>
 +
 +
Pour démarrer [[R|R]] dans un terminal, tapez
 +
<pre>
 +
R
 +
</pre>
 +
pour la version non-graphique (ligne de commandes), ou encore
 +
<pre>
 +
xR
 +
</pre>
 +
pour la version à interface graphique.
 +
 +
*Installation de snow sur votre compte DMS : À la première utilisation, snow n'est probablement pas intallé. Pour vérifier, appelez dans [[R|R]]
 +
<pre>
 +
require(snow)
 +
</pre>
 +
et si snow n'est pas installé, un message d'erreur s'affichera
 +
<pre>
 +
Le chargement a nécessité le package : snow
 +
Warning message:
 +
In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE,  :
 +
  aucun package nommé ‘snow’ n'est trouvé
 +
</pre>
 +
Vous pouvez aussi appeler <code>library()</code> et parcourir la liste pour voir si snow s'y trouve.
 +
 +
L'installation se fait en appelant
 +
<pre>
 +
install.packages("snow")
 +
</pre>
 +
Ensuite [[R|R]] vous demandera si vous voulez installer snow dans une librairie personnelle et s'il peut l'installer dans un certain répertoire (probablement dans <code>/home/usager/R/x86_64-pc-linux-gnu-library</code> ). Répondez oui aux deux questions. Vous devrez par la suite choisir un miroir CRAN; prenez-en un près d'où vous êtes (probablement USA (NY)). L'installation devrait se terminer après quelques instants. Vous pouvez maintenant appeler snow par <code>require(snow)</code>.
 +
 +
=== Exemple ===
 +
L'exemple qui suit a pour but de montrer comment utiliser les fonctions du package <tt>snow</tt> afin de lancer des calculs en parallèle. Les calculs sont d'abord effectués localement, c'est-à-dire sur la machine de l'utilisateur, puis effectués par plusieurs ordinateurs (''cluster'').
  
* Faites juste «Enter» lorsque vous voyez&nbsp;:
+
Pour tester l'exemple par vous-même, &laquo;&nbsp;copiez-collez&nbsp;&raquo; les commandes suivantes dans [[R|R]]&nbsp;:
  
 
<pre>
 
<pre>
Enter passphrase (empty for no passphrase):  
+
require(snow)
 +
 
 +
# La fonction myfunc définie ci-dessous exécute en boucle de courts calculs. Il est
 +
# important de noter que les itérations de la boucle sont indépendantes les unes des
 +
# autres : c'est ce qui permet la parallélisation.
 +
 
 +
myfunc <- function(M=1000) {
 +
  decision <- 0
 +
  for (i in 1:M) {
 +
    x <- rnorm(100)
 +
    if (shapiro.test(x)$p < 0.05) decision <- decision + 1
 +
  }
 +
  return(decision)
 +
}
 +
 
 +
# Ci-dessous, on chronomètre le temps d'exécution de la fonction "myfunc".
 +
 
 +
system.time({
 +
  M <- 6000000
 +
  decision <- myfunc(M)
 +
  print(decision/M)
 +
})
 +
 
 +
# Temps d'exécution : 814 s (~ 14 min.).
 +
 
 +
# Les calculs sont maintenant divisés sur plusieurs ordinateurs (10 dans cet exemple).
 +
# Avant de lancer les calculs, le script "snowdms.sh" est appelé afin de dresser une
 +
# liste de machines à utiliser pour les calculs.
 +
 
 +
nbcpus <- 10
 +
cpus <- strsplit(system(paste("snowdms.sh ",nbcpus),intern=TRUE),split=" ")[[1]]
 +
 
 +
  # La liste des machines (cpus) est enregistrée dans "cpus.csv".
 +
write(cpus, file="~/cpus.csv", 10, append=TRUE, "\t") 
 +
system.time({
 +
  # Fonction pour démarrer un "cluster" snow.
 +
  cl <- makeCluster(cpus, type = "SOCK") 
 +
  # Initialisation d'un flux de nombres aléatoires pour le cluster.             
 +
  clusterSetupSPRNG(cl)                               
 +
  M <- 6000000                 
 +
 
 +
  # La fonction "myfunc" est appelée sur chaque machine de la liste, avec
 +
  # round(M/nbcpus) comme argument. "out[[i]]" contient les résultats
 +
  out <- clusterCall(cl, myfunc, round(M/nbcpus))     
 +
  # obtenus par la i-ème machine.                                                       
 +
 
 +
  # Arrêt du cluster.
 +
  stopCluster(cl)                                     
 +
  decision <- 0
 +
  for (cpus in 1:nbcpus) {
 +
    decision <- decision + out[[cpus]]
 +
  }
 +
  print(decision/(round(M/nbcpus)*nbcpus))
 +
})
 +
 
 +
# Quelques temps obtenus selon le nb de cpus
 +
 
 +
# nbcpus <- 10 : 111 s
 +
# nbcpus <- 15 : 84 s
 +
# nbcpus <- 20 : 70 s
 +
# nbcpus <- 80 : 58 s
 +
 
 +
# Les calculs s'effectuent plus rapidement avec un grand nombre de machines.
 
</pre>
 
</pre>
  
* De nouveau faites juste «Enter» lorsque vous voyez&nbsp;:
+
==== Remarques ====
  
 +
* Des messages tels que
 
<pre>
 
<pre>
Enter same passphrase again:
+
The authenticity of host 'leopard (132.204.53.53)' can't be established.
 +
RSA key fingerprint is a6:a3:80:c8:52:22:d8:de:be:5a:d8:f4:04:cf:2c:01.
 +
Are you sure you want to continue connecting (yes/no)?
 
</pre>
 
</pre>
 +
apparaissent lors d'une première connexion à une machine. Répondez par &laquo;&nbsp;yes&nbsp;&raquo; pour mémoriser la clé d'identification de la machine.
  
* Lancer R, en tapant&nbsp;:
+
* La liste des machines choisies par le script <tt>snowdms.sh</tt> est enregistrée dans un fichier dans le compte de l'usager (<tt>~/cpus.csv</tt>). Ainsi, si vous avez démarrées des simulations par erreur, vous savez sur quelles machines vous brancher (par [[La_commande_ssh|<tt>ssh</tt>]]) afin de stopper ces simulations (commande [[Commandes_élémentaires#kill|<tt>kill</tt>]]).
  
 +
* La fonction <tt>clusterCall</tt> ne permet pas de faire varier l'argument passé à la fonction&nbsp;: chaque machine de la liste exécutera la ''même fonction'' avec le ''même argument''. D'autres fonctions, comme <tt>clusterApply</tt> par exemple, offrent la possibilité de varier l'argument. Pour plus de détails, consultez le [http://cran.r-project.org/web/packages/snow/snow.pdf manuel du package].
 +
 +
* Lorsque le package snow est utilisé pour paralléliser une fonction sur <tt>n</tt> CPUs, il faut imaginer que <tt>n</tt> nouvelles sessions de R indépendantes sont ainsi démarrées. Il est donc nécessaire d'inclure la définition de ''toutes'' les fonctions à employer à l'intérieur de la fonction parallélisée. Ceci est aussi vrai pour les packages qu'il convient de relancer avec <tt>library()</tt> (ou encore <tt>require()</tt>). Finalement, une situation similaire se produit pour le répertoire actif (''working directory''), puisque celui par défaut sera utilisé pour chaque nouvelle session. Dans le doute, utilisez les chemins complets.
 +
 +
<!--* Lorsqu'on lance le package '''snow''', par exemple sur 4 CPUs,  on peut imaginer qu'on a en fait lancé 4 nouvelles sessions de R sur 4 machines différentes. Il faut donc tout inclure(réinitialiser). C'est-à-dire, si la fonction qui est parallélisée appelle («''call''») une autre fonction qui a été définie auparavant il faut qu'elle soit définie à l’intérieure de la fonction principale! (en d'autre mot il faut la sourcer). C'est ainsi pour les «''packages''» où il faut les relancer avec library() ( ou require() ). Aussi, il ne faut pas oublier que c'est la même situation pour le «''working directory''».-->
 +
=== Nombre de CPUs ===
 +
Le rendement en terme de temps de calcul dépend du type de processus que vous lancez sur les machines.
 +
 +
Dans l'exemple ci-haut, on observe que plus on utilise de CPUs, plus vite s'effectuent les calculs. Mais il faut faire attention puisque, d'un calcul à l'autre, le gain risque de changer. En d'autres termes, les calculs ne sont pas toujours facilement parallélisables.
 +
 +
Il ne faut pas oublier que l'envoi de calculs sur plusieurs machines prend du temps (communication / transfert), donc il faut préalablement vous assurer que l'effort en vaille la chandelle&nbsp;: effectuez quelques tests d'abord avant de démarrer vos calculs lourds. Parfois, il est préférable de lancer un programme qui n'est pas trop lourd sur une seule machine.
 +
 +
==== Exemple de tous les jours ====
 +
Disons qu'on veut écrire une lettre de deux lignes. À la place de l'écrire, vous vous déplacer 3 étages du Pavillon André-Aisenstadt pour aller voir votre ami au troisième étage pour lui demander de l'aide. Pendant ce temps-là vous auriez pu avoir déjà écrit votre lettre de deux lignes. Mais, par exemple si vous avez une lettre de 20 page à écrire, en ce moment-là cela vaut la peine de se déplacer trois étages pour allez demander de l'aide à son ami.   
 +
 +
=== Génération de nombres aléatoires ===
 +
Lorsque vous générez des nombres aléatoires pour plusieurs machines, vous devez vous assurez que chaque machine emploie une suite de nombres différente, sans quoi les résultats de toutes les machines seront identiques.
 +
 +
Pour ce faire, vous pourriez être tentés de procéder comme suit&nbsp;:
 
<pre>
 
<pre>
/usr/local/stat/R/R-3.0.0/bin/R
+
# Génération de trois nombres aléatoires avec "runif".
 +
 
 +
clusterCall(cl, runif, 3)
 +
 
 +
[[1]]
 +
[1] 0.08496597 0.35232298 0.60300751
 +
 
 +
[[2]]
 +
[1] 0.08496597 0.35232298 0.60300751
 
</pre>
 
</pre>
  
==Exemple explicatif==
+
Mais, comme on l'aperçoit, la suite générée est identique sur toutes les machines (ici <tt>cl</tt> est configuré pour 2 machines).
 +
 
 +
Pour régler ce problème, la démarche à suivre est d'utiliser la fonction <tt>clusterSetupSPRNG</tt> prévue à cet effet, comme dans [[Snow#Exemple|l'exemple]] ci-haut. Avec cette fonction, les suites générées par les machines sont différentes&nbsp;:
 +
<pre>
 +
# Génération de trois nombres aléatoires avec "runif" et "clusterSetupSPRNG".
 +
 
 +
clusterSetupSPRNG(cl)
 +
clusterCall(cl, runif, 3)
 +
 
 +
[[1]]
 +
[1] 0.749391854 0.007316102 0.152742874
 +
 
 +
[[2]]
 +
[1] 0.8424790 0.8896625 0.2256776
 +
</pre>
 +
Pour plus de détails, consultez les [[Snow#Références_externes|références externes]] ci-dessous.
  
 
== Voir aussi ==
 
== Voir aussi ==
  
=== '''Références externes''' ===
+
=== Articles connexes ===
 +
<div class="inline">
 +
* [[Simulations|Simulations]]
 +
* [[R|R]]
 +
* [[Logiciels|Logiciels au Département]]
 +
</div>
 +
 
 +
=== Références externes ===
 +
<div class="inline">
 +
* http://cran.r-project.org/web/packages/snow/snow.pdf
 +
* http://homepage.stat.uiowa.edu/~luke/R/cluster/cluster.html
 
* http://www.sfu.ca/~sblay/R/snow.html
 
* http://www.sfu.ca/~sblay/R/snow.html
 +
</div>

Version actuelle datée du 3 décembre 2020 à 20:54

Le package snow (Simple Network Of Workstations) offre la possibilité d'exécuter du code R en parallèle sur plusieurs ordinateurs. Les calculs statistiques lourds peuvent ainsi être accélérés de manière significative.

Configuration

Attention.png

Avant d'entreprendre des simulations numériques sur les machines du Département, vous êtes encouragé à consulter la page Simulations, en particulier la section traitant des consignes à respecter.

Ci-dessous, vous trouverez les instructions pour la configuration initiale de snow pour votre compte au DMS.

  • Tapez
makeKey.sh

dans un terminal de commandes. La ligne précédente exécute le script makeKey.sh qui configure le système ssh afin que vous puissiez vous brancher à d'autres machines du Département sans qu'on vous demande votre mot de passe.

  • Si la question
Generating public/private rsa key pair.
/home/bayani/.ssh/id_rsa already exists.
Overwrite (y/n)? 

apparaît, répondez par « y ».

  • Appuyez sur Entrée (Enter) lorsque vous apercevez les messages
Enter passphrase (empty for no passphrase): 

et

Enter same passphrase again:

Pour démarrer R dans un terminal, tapez

R 

pour la version non-graphique (ligne de commandes), ou encore

xR

pour la version à interface graphique.

  • Installation de snow sur votre compte DMS : À la première utilisation, snow n'est probablement pas intallé. Pour vérifier, appelez dans R
require(snow)

et si snow n'est pas installé, un message d'erreur s'affichera

Le chargement a nécessité le package : snow
Warning message:
In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE,  :
  aucun package nommé ‘snow’ n'est trouvé

Vous pouvez aussi appeler library() et parcourir la liste pour voir si snow s'y trouve.

L'installation se fait en appelant

install.packages("snow")

Ensuite R vous demandera si vous voulez installer snow dans une librairie personnelle et s'il peut l'installer dans un certain répertoire (probablement dans /home/usager/R/x86_64-pc-linux-gnu-library ). Répondez oui aux deux questions. Vous devrez par la suite choisir un miroir CRAN; prenez-en un près d'où vous êtes (probablement USA (NY)). L'installation devrait se terminer après quelques instants. Vous pouvez maintenant appeler snow par require(snow).

Exemple

L'exemple qui suit a pour but de montrer comment utiliser les fonctions du package snow afin de lancer des calculs en parallèle. Les calculs sont d'abord effectués localement, c'est-à-dire sur la machine de l'utilisateur, puis effectués par plusieurs ordinateurs (cluster).

Pour tester l'exemple par vous-même, « copiez-collez » les commandes suivantes dans R :

require(snow)

# La fonction myfunc définie ci-dessous exécute en boucle de courts calculs. Il est 
# important de noter que les itérations de la boucle sont indépendantes les unes des 
# autres : c'est ce qui permet la parallélisation.

myfunc <- function(M=1000) {
  decision <- 0
  for (i in 1:M) {
    x <- rnorm(100)
    if (shapiro.test(x)$p < 0.05) decision <- decision + 1
  }
  return(decision)
}

# Ci-dessous, on chronomètre le temps d'exécution de la fonction "myfunc".

system.time({
  M <- 6000000
  decision <- myfunc(M)
  print(decision/M)
})

# Temps d'exécution : 814 s (~ 14 min.).

# Les calculs sont maintenant divisés sur plusieurs ordinateurs (10 dans cet exemple).
# Avant de lancer les calculs, le script "snowdms.sh" est appelé afin de dresser une 
# liste de machines à utiliser pour les calculs.

nbcpus <- 10
cpus <- strsplit(system(paste("snowdms.sh ",nbcpus),intern=TRUE),split=" ")[[1]]

  # La liste des machines (cpus) est enregistrée dans "cpus.csv".
write(cpus, file="~/cpus.csv", 10, append=TRUE, "\t")   
system.time({
  # Fonction pour démarrer un "cluster" snow.
  cl <- makeCluster(cpus, type = "SOCK")  
  # Initialisation d'un flux de nombres aléatoires pour le cluster.              
  clusterSetupSPRNG(cl)                                 
  M <- 6000000                   

  # La fonction "myfunc" est appelée sur chaque machine de la liste, avec 
  # round(M/nbcpus) comme argument. "out[[i]]" contient les résultats
  out <- clusterCall(cl, myfunc, round(M/nbcpus))       
  # obtenus par la i-ème machine.                                                        

  # Arrêt du cluster.
  stopCluster(cl)                                       
  decision <- 0
  for (cpus in 1:nbcpus) {
    decision <- decision + out[[cpus]]
  }
  print(decision/(round(M/nbcpus)*nbcpus))
})

# Quelques temps obtenus selon le nb de cpus

# nbcpus <- 10 : 111 s
# nbcpus <- 15 : 84 s
# nbcpus <- 20 : 70 s
# nbcpus <- 80 : 58 s

# Les calculs s'effectuent plus rapidement avec un grand nombre de machines.

Remarques

  • Des messages tels que
The authenticity of host 'leopard (132.204.53.53)' can't be established.
RSA key fingerprint is a6:a3:80:c8:52:22:d8:de:be:5a:d8:f4:04:cf:2c:01.
Are you sure you want to continue connecting (yes/no)?

apparaissent lors d'une première connexion à une machine. Répondez par « yes » pour mémoriser la clé d'identification de la machine.

  • La liste des machines choisies par le script snowdms.sh est enregistrée dans un fichier dans le compte de l'usager (~/cpus.csv). Ainsi, si vous avez démarrées des simulations par erreur, vous savez sur quelles machines vous brancher (par ssh) afin de stopper ces simulations (commande kill).
  • La fonction clusterCall ne permet pas de faire varier l'argument passé à la fonction : chaque machine de la liste exécutera la même fonction avec le même argument. D'autres fonctions, comme clusterApply par exemple, offrent la possibilité de varier l'argument. Pour plus de détails, consultez le manuel du package.
  • Lorsque le package snow est utilisé pour paralléliser une fonction sur n CPUs, il faut imaginer que n nouvelles sessions de R indépendantes sont ainsi démarrées. Il est donc nécessaire d'inclure la définition de toutes les fonctions à employer à l'intérieur de la fonction parallélisée. Ceci est aussi vrai pour les packages qu'il convient de relancer avec library() (ou encore require()). Finalement, une situation similaire se produit pour le répertoire actif (working directory), puisque celui par défaut sera utilisé pour chaque nouvelle session. Dans le doute, utilisez les chemins complets.

Nombre de CPUs

Le rendement en terme de temps de calcul dépend du type de processus que vous lancez sur les machines.

Dans l'exemple ci-haut, on observe que plus on utilise de CPUs, plus vite s'effectuent les calculs. Mais il faut faire attention puisque, d'un calcul à l'autre, le gain risque de changer. En d'autres termes, les calculs ne sont pas toujours facilement parallélisables.

Il ne faut pas oublier que l'envoi de calculs sur plusieurs machines prend du temps (communication / transfert), donc il faut préalablement vous assurer que l'effort en vaille la chandelle : effectuez quelques tests d'abord avant de démarrer vos calculs lourds. Parfois, il est préférable de lancer un programme qui n'est pas trop lourd sur une seule machine.

Exemple de tous les jours

Disons qu'on veut écrire une lettre de deux lignes. À la place de l'écrire, vous vous déplacer 3 étages du Pavillon André-Aisenstadt pour aller voir votre ami au troisième étage pour lui demander de l'aide. Pendant ce temps-là vous auriez pu avoir déjà écrit votre lettre de deux lignes. Mais, par exemple si vous avez une lettre de 20 page à écrire, en ce moment-là cela vaut la peine de se déplacer trois étages pour allez demander de l'aide à son ami.

Génération de nombres aléatoires

Lorsque vous générez des nombres aléatoires pour plusieurs machines, vous devez vous assurez que chaque machine emploie une suite de nombres différente, sans quoi les résultats de toutes les machines seront identiques.

Pour ce faire, vous pourriez être tentés de procéder comme suit :

# Génération de trois nombres aléatoires avec "runif".

clusterCall(cl, runif, 3)

[[1]]
[1] 0.08496597 0.35232298 0.60300751

[[2]]
[1] 0.08496597 0.35232298 0.60300751

Mais, comme on l'aperçoit, la suite générée est identique sur toutes les machines (ici cl est configuré pour 2 machines).

Pour régler ce problème, la démarche à suivre est d'utiliser la fonction clusterSetupSPRNG prévue à cet effet, comme dans l'exemple ci-haut. Avec cette fonction, les suites générées par les machines sont différentes :

# Génération de trois nombres aléatoires avec "runif" et "clusterSetupSPRNG".

clusterSetupSPRNG(cl)
clusterCall(cl, runif, 3)

[[1]]
[1] 0.749391854 0.007316102 0.152742874

[[2]]
[1] 0.8424790 0.8896625 0.2256776

Pour plus de détails, consultez les références externes ci-dessous.

Voir aussi

Articles connexes

Références externes


La dernière modification de cette page a été faite le 3 décembre 2020 à 20:54.