0Pricing
R Academy · Leçon

Paquet parallel et detectCores()

Lancez des grappes dérivées ou à sockets et répartissez le travail entre les cœurs du CPU.

Paquet parallel et detectCores() est une leçon R Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage R Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours R Academy comprend 4 leçons au total.

Pourquoi l’informatique parallèle ?

Les ordinateurs modernes disposent de plusieurs cœurs CPU. Par défaut, R s’exécute sur un seul cœur et laisse les autres inactifs. Le paquet parallel (intégré à R) vous permet d’exploiter tous les cœurs afin d’accélérer les calculs répétitifs.

# Check how many cores your machine has
library(parallel)

total_cores <- detectCores()
logical_cores <- detectCores(logical = TRUE)
physical_cores <- detectCores(logical = FALSE)

cat('Total logical cores:', total_cores, '
')
cat('Physical cores:', physical_cores, '
')

makeCluster et stopCluster

makeCluster(n) crée n processus de travail. Appelez toujours stopCluster(cl) lorsque vous avez terminé afin de libérer les ressources. Une convention courante consiste à utiliser detectCores() - 1 pour laisser un cœur disponible pour l’OS.

library(parallel)

# Spawn workers (leave 1 core for system)
n_cores <- detectCores() - 1
cl <- makeCluster(n_cores)

cat('Cluster created with', n_cores, 'workers\n')

# Always clean up!
stopCluster(cl)
cat('Cluster stopped.\n')

clusterExport : partager des variables

Les processus de travail disposent de leur propre espace mémoire et ne peuvent pas accéder à votre environnement global. Utilisez clusterExport(cl, varlist) pour copier les objets nommés du processus maître vers tous les processus de travail.

library(parallel)
cl <- makeCluster(2)

# Define a variable and a function in the master
base_value <- 100
add_base <- function(x) x + base_value

# Export them to workers
clusterExport(cl, varlist = c('base_value', 'add_base'))

# Now workers can use them
result <- parLapply(cl, 1:4, function(x) add_base(x))
cat(unlist(result), '\n')  # 101 102 103 104

stopCluster(cl)

clusterEvalQ : exécuter le code de configuration

clusterEvalQ(cl, expr) évalue une expression sur chaque processus de travail — ce qui est utile pour charger des paquets ou importer des fichiers auxiliaires sur tous les nœuds avant le début du calcul principal.

library(parallel)
cl <- makeCluster(2)

# Load a package on every worker
clusterEvalQ(cl, {
  library(stats)
  set.seed(42)  # set seed per worker
})

# Each worker can now use stats functions
result <- parLapply(cl, 1:4, function(n) rnorm(n, mean = 0, sd = 1))
result[[1]]  # one random normal value

stopCluster(cl)

parLapply : lapply en parallèle

parLapply(cl, X, FUN) est l’équivalent parallèle de lapply(). Il répartit les éléments de X entre les processus de travail et rassemble les résultats sous forme de liste. Il fonctionne sur toutes les plateformes (Windows, macOS, Linux).

library(parallel)
cl <- makeCluster(2)

# Simulate slow computation: sleep 0.1s per item
clusterEvalQ(cl, Sys.sleep)

slow_square <- function(x) {
  Sys.sleep(0.05)
  x^2
}

clusterExport(cl, 'slow_square')

system.time(
  result <- parLapply(cl, 1:8, slow_square)
)
cat(unlist(result), '\n')  # 1 4 9 16 25 36 49 64

stopCluster(cl)

parSapply : résultats simplifiés

parSapply(cl, X, FUN) est la version parallèle de sapply(). Il simplifie automatiquement le résultat en vecteur ou en matrice lorsque cela est possible, ce qui le rend pratique lorsque votre fonction renvoie une valeur scalaire.

library(parallel)
cl <- makeCluster(2)

# parSapply returns a simplified vector
squares <- parSapply(cl, 1:10, function(x) x^2)
cat(squares, '\n')  # 1 4 9 16 25 36 49 64 81 100

# Returns a matrix if FUN returns a vector of same length
stats_result <- parSapply(cl, 1:4, function(n) {
  x <- rnorm(100)
  c(mean = mean(x), sd = sd(x))
})
print(stats_result)  # 2x4 matrix

stopCluster(cl)

mclapply : parallélisme par duplication de processus

mclapply() utilise la duplication par embranchement : il copie le processus R actuel au lieu d’en créer de nouveaux, ce qui accélère le démarrage et donne aux processus de travail un accès implicite à l’environnement parent. Cependant, il ne fonctionne que sous Unix/macOS, pas sous Windows.

library(parallel)

# mclapply: Unix/macOS only
# Workers inherit the parent environment automatically
base_value <- 42

if (.Platform$OS.type != 'windows') {
  result <- mclapply(
    1:8,
    function(x) x * base_value,  # base_value visible without export
    mc.cores = 4
  )
  cat(unlist(result), '\n')
} else {
  cat('mclapply not supported on Windows. Use parLapply instead.\n')
}

Comparer les performances séquentielles et parallèles

Le parallélisme entraîne un surcoût : le démarrage du groupe de processus, la sérialisation des données et la communication entre processus prennent tous du temps. Il n’est avantageux que lorsque le calcul de chaque élément est suffisamment coûteux pour compenser ce surcoût.

library(parallel)
cl <- makeCluster(2)

# Task: compute 100 iterations of matrix multiply
heavy_task <- function(n) {
  m <- matrix(rnorm(200), nrow = 100)
  sum(m %*% t(m))
}
clusterExport(cl, 'heavy_task')

seq_time <- system.time(lapply(1:20, heavy_task))[['elapsed']]
par_time <- system.time(parLapply(cl, 1:20, heavy_task))[['elapsed']]

cat('Sequential:', round(seq_time, 3), 's\n')
cat('Parallel:  ', round(par_time, 3), 's\n')
cat('Speedup:   ', round(seq_time / par_time, 2), 'x\n')

stopCluster(cl)

Initialiser le RNG en parallèle

La génération de nombres aléatoires en parallèle est délicate : chaque processus de travail a besoin d’un flux indépendant et reproductible. Utilisez clusterSetRNGStream(cl, seed) avec le générateur L'Ecuyer-CMRG pour obtenir une génération aléatoire parallèle reproductible.

library(parallel)
cl <- makeCluster(2)

# Set reproducible RNG streams across workers
clusterSetRNGStream(cl, iseed = 123)

# Each worker uses its own independent random stream
results1 <- parSapply(cl, 1:6, function(i) rnorm(1))

# Reset and repeat — same results
clusterSetRNGStream(cl, iseed = 123)
results2 <- parSapply(cl, 1:6, function(i) rnorm(1))

cat('Run 1:', round(results1, 4), '\n')
cat('Run 2:', round(results2, 4), '\n')
cat('Identical:', identical(results1, results2), '\n')

stopCluster(cl)

Gérer les erreurs dans les groupes de processus

Si un processus de travail déclenche une erreur, parLapply() la relance dans le processus maître. Encapsulez les appels dans tryCatch() à l’intérieur de la fonction, ou utilisez tryCatch() autour de l’appel parLapply() entier pour gérer les échecs correctement.

library(parallel)
cl <- makeCluster(2)

# Wrap risky code inside the worker function
safe_log <- function(x) {
  tryCatch(
    log(x),
    warning = function(w) NA_real_,
    error   = function(e) NA_real_
  )
}
clusterExport(cl, 'safe_log')

# -1 produces NaN warning, 'a' produces an error
input <- list(4, 9, -1, 'a', 16)
result <- parLapply(cl, input, safe_log)
cat(unlist(result), '\n')  # 1.386 2.197 NaN NA 2.773

stopCluster(cl)

Modèle pratique de parallélisme

Voici un flux de travail parallèle complet et idiomatique : détecter le nombre de cœurs, créer le groupe de processus, exporter les dépendances, exécuter le calcul, rassembler les résultats et toujours arrêter le groupe — même en cas d’erreur — avec on.exit().

library(parallel)

run_parallel <- function(data, fn, n_workers = detectCores() - 1) {
  cl <- makeCluster(n_workers)
  on.exit(stopCluster(cl))  # guaranteed cleanup

  clusterExport(cl, 'fn', envir = environment())

  result <- parLapply(cl, data, fn)
  result
}

# Example: bootstrap mean estimation
samples <- lapply(1:100, function(i) rnorm(50, mean = 5, sd = 2))
means <- run_parallel(samples, mean)

cat('Grand mean:', round(mean(unlist(means)), 3), '\n')
cat('95% CI: [',
    round(quantile(unlist(means), 0.025), 3), ',',
    round(quantile(unlist(means), 0.975), 3), ']\n')

Vérification rapide

Quelle fonction devez-vous utiliser pour garantir que stopCluster(cl) sera appelé même si une erreur se produit dans votre code parallèle ?

Récapitulatif : paquet parallel

À retenir :

  • detectCores() indique le nombre de cœurs disponibles ; utilisez detectCores() - 1 pour définir la taille du groupe
  • makeCluster(n) / stopCluster(cl) gèrent le cycle de vie des processus de travail
  • clusterExport() copie les objets ; clusterEvalQ() exécute le code de configuration sur les processus de travail
  • parLapply() / parSapply() sont des itérateurs parallèles compatibles avec plusieurs plateformes
  • mclapply() est réservé à Unix, mais son démarrage est plus rapide grâce à la duplication de processus
  • Utilisez on.exit(stopCluster(cl)) pour garantir le nettoyage
  • Le parallélisme n’est avantageux que pour les tâches nécessitant beaucoup de calculs
# Minimal reproducible parallel pattern
library(parallel)

cl <- makeCluster(max(1, detectCores() - 1))
on.exit(stopCluster(cl))

clusterSetRNGStream(cl, iseed = 42)
result <- parSapply(cl, 1:8, function(x) x^2 + rnorm(1, 0, 0.1))
cat(round(result, 2), '\n')

Questions Fréquemment Posées

La leçon « Paquet parallel et detectCores() » est-elle gratuite ?

Oui — le texte complet de « Paquet parallel et detectCores() » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours R Academy, passe à CoddyKit PRO. Le cours R Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Paquet parallel et detectCores() » ?

Lancez des grappes dérivées ou à sockets et répartissez le travail entre les cœurs du CPU. Tu pratiques R Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer R Academy ?

Aucune expérience préalable n'est requise. R Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Paquet parallel et detectCores() » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon R Academy ?

Oui. Chaque leçon R Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Paquet parallel et detectCores()
  2. Le cadriciel future
  3. furrr : opérations parallèles avec purrr
  4. Déboguer et équilibrer la charge du code parallèle
← Retour à R Academy