Différences entre les versions de « Snow »

Ligne 45 : Ligne 45 :
 
<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.
  
 
myfunc <- function(M=1000) {
 
myfunc <- function(M=1000) {

Version du 25 juin 2013 à 14:46

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

  • 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 la version 3.0 de R, qui inclut snow, entrez

/usr/local/stat/R/R-3.0.0/bin/R 

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

/usr/local/stat/R/R-3.0.0/lib64/R/library/JGR/scripts/run

pour la version à interface graphique.

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, tapez 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.

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 10 machines à utiliser pour les calculs.

nbcpus <- 10
cpus <- strsplit(system(paste("snowdms.sh ",nbcpus),intern=TRUE),split=" ")[[1]]  # La variable "cpus" contient la liste des machines.
write(cpus, file="~/cpus.csv", 10, append=TRUE, "\t")      # La liste des machines choisies est enregistrée dans "cpus.csv".

system.time({
  cl <- makeCluster(cpus, type = "SOCK") # Fonctions pour démarrer un "cluster" snow.
  clusterSetupSPRNG(cl)                  # Initialisation d'un flux de nombres aléatoires pour le cluster.
  M <- 6000000                   
  out <- clusterCall(cl, myfunc, round(M/nbcpus))  # Configuration du cluster
  stopCluster(cl)                                  # Fonctions pour arrêter un cluster de Snow.
  decision <- 0
  for (cpus in 1:nbcpus) {
    decision <- decision + out[[cpus]]
  }
  print(decision/(round(M/nbcpus)*nbcpus))
})

# Avec nbcpus <- 10: 111s
# Avec nbcpus <- 15: 84s
# Avec nbcpus <- 20: 70s
# Avec nbcpus <- 80: 58s
# On voit ici qu'au fur et à mesure qu'on augmente le nombre de CPUs le processus va plus vite. 

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)?

peuvent apparaître. Il faut alors répondre par «yes». Ces messages n'apparaîtont qu'une seule fois (pour une machine donnée).

  • 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 tuer vos simulations (commande kill).
  • La fonction clusterCall ne permet pas de faire varier l'argument passé à la fonction, c'est-à-dire que chaque machine de la liste exécutera la même fonction avec le même argument. Cependant, d'autres fonctions, comme clusterApply par exemple, offrent cette possibilité. Pour plus de détails, consultez le manuel du package.

Nombre de CPUs

Le rendement en terme du temps de calcul dépend du type de processus que vous allez lancer sur les machines. On voit dans l'exemple que plus on utilise de CPUs, plus vite iront les calculs. Mais il faut faire attention puisque, tout dépendamment du type de calculs à réaliser, le gain risque de changer. Il ne faut pas oublier que l'envoi des calculs sur plusieurs CPUs prend du temps, alors il faut que votre processus vaille le coup d'être envoyer sur plusieurs CPUs (c.-à-d. votre programme est bien parallélisable). Sinon, c'est mieux de lancer un programme qui est pas trop grand 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érer des nombres aléatoires

Les générateurs des nombres aléatoires par défaut sont susceptibles d'être très corrélés. Par exemple :

> clusterCall(cl, runif, 3)
[[1]]
[1] 0.08496597 0.35232298 0.60300751

[[2]]
[1] 0.08496597 0.35232298 0.60300751

# Génération de trois nombres aléatoires avec «runif»

Une façon rapide (mais pas propre) d'aborder ce problème est de faire :

clusterApply(cl, runif(length(cl),max=10000000), set.seed)

Une meilleure démarche est d'utiliser un «package» générateur de nombre aléatoire parallèle; comme l'exemple explicatif ci-haut.

Un petit exemple :

> clusterSetupRNG(cl)
> clusterCall(cl, runif, 3)
[[1]]
[1] 0.749391854 0.007316102 0.152742874

[[2]]
[1] 0.8424790 0.8896625 0.2256776

# Ici clusterSetupRNG(cl), initialise le flux de nombres aléatoires uniformes indépendantes pour un cluster de snow.   

Pour la source de cette section et plus de détails consulter 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 25 juin 2013 à 14:46.