Différences entre les versions de « Snow »
(56 versions intermédiaires par 4 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
− | Les calculs statistiques | + | 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__ | ||
− | + | === 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]]. | ||
+ | |} | ||
− | + | Ci-dessous, vous trouverez les instructions pour la configuration initiale de <tt>snow</tt> pour votre compte au DMS. | |
− | |||
− | |||
− | * | + | * 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 « y ». | ||
− | * | + | * Appuyez sur <tt>Entrée</tt> (''Enter'') lorsque vous apercevez les messages |
− | |||
<pre> | <pre> | ||
Enter passphrase (empty for no passphrase): | Enter passphrase (empty for no passphrase): | ||
+ | </pre> | ||
+ | et | ||
+ | <pre> | ||
+ | Enter same passphrase again: | ||
</pre> | </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> | <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> | </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> | <pre> | ||
− | + | install.packages("snow") | |
</pre> | </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''). | ||
− | + | Pour tester l'exemple par vous-même, « copiez-collez » les commandes suivantes dans [[R|R]] : | |
− | |||
<pre> | <pre> | ||
require(snow) | 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) { | myfunc <- function(M=1000) { | ||
Ligne 48 : | Ligne 84 : | ||
} | } | ||
+ | # Ci-dessous, on chronomètre le temps d'exécution de la fonction "myfunc". | ||
system.time({ | system.time({ | ||
Ligne 55 : | Ligne 92 : | ||
}) | }) | ||
− | # Temps | + | # 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 | nbcpus <- 10 | ||
cpus <- strsplit(system(paste("snowdms.sh ",nbcpus),intern=TRUE),split=" ")[[1]] | 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({ | system.time({ | ||
− | cl <- makeCluster(cpus, type = "SOCK") # | + | # Fonction pour démarrer un "cluster" snow. |
− | clusterSetupSPRNG(cl) | + | cl <- makeCluster(cpus, type = "SOCK") |
+ | # Initialisation d'un flux de nombres aléatoires pour le cluster. | ||
+ | clusterSetupSPRNG(cl) | ||
M <- 6000000 | M <- 6000000 | ||
− | out <- clusterCall(cl, myfunc, round(M/nbcpus)) | + | |
− | stopCluster(cl) | + | # 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 | decision <- 0 | ||
for (cpus in 1:nbcpus) { | for (cpus in 1:nbcpus) { | ||
Ligne 74 : | Ligne 124 : | ||
}) | }) | ||
− | # | + | # 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> | ||
− | + | ==== Remarques ==== | |
+ | |||
+ | * Des messages tels que | ||
<pre> | <pre> | ||
The authenticity of host 'leopard (132.204.53.53)' can't be established. | The authenticity of host 'leopard (132.204.53.53)' can't be established. | ||
Ligne 87 : | Ligne 142 : | ||
Are you sure you want to continue connecting (yes/no)? | Are you sure you want to continue connecting (yes/no)? | ||
</pre> | </pre> | ||
+ | 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 <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 : 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 : 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 : | |
<pre> | <pre> | ||
− | + | # Génération de trois nombres aléatoires avec "runif". | |
+ | |||
+ | clusterCall(cl, runif, 3) | ||
+ | |||
[[1]] | [[1]] | ||
[1] 0.08496597 0.35232298 0.60300751 | [1] 0.08496597 0.35232298 0.60300751 | ||
Ligne 105 : | Ligne 175 : | ||
[[2]] | [[2]] | ||
[1] 0.08496597 0.35232298 0.60300751 | [1] 0.08496597 0.35232298 0.60300751 | ||
+ | </pre> | ||
− | + | 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 : | |
<pre> | <pre> | ||
− | + | # Génération de trois nombres aléatoires avec "runif" et "clusterSetupSPRNG". | |
− | |||
− | + | clusterSetupSPRNG(cl) | |
+ | clusterCall(cl, runif, 3) | ||
− | |||
− | |||
− | |||
− | |||
− | |||
[[1]] | [[1]] | ||
[1] 0.749391854 0.007316102 0.152742874 | [1] 0.749391854 0.007316102 0.152742874 | ||
Ligne 126 : | Ligne 191 : | ||
[[2]] | [[2]] | ||
[1] 0.8424790 0.8896625 0.2256776 | [1] 0.8424790 0.8896625 0.2256776 | ||
− | |||
− | |||
</pre> | </pre> | ||
− | Pour | + | Pour plus de détails, consultez les [[Snow#Références_externes|références externes]] ci-dessous. |
== Voir aussi == | == Voir aussi == | ||
− | === | + | === 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://cran.r-project.org/web/packages/snow/snow.pdf | ||
* http://homepage.stat.uiowa.edu/~luke/R/cluster/cluster.html | * 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
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.