R Academy · Aula

Pacote parallel e detectCores()

Inicie agrupamentos derivados ou por soquetes e distribua o trabalho entre núcleos de CPU.

Aula 1 de 413 etapas

Pacote parallel e detectCores() é uma aula grátis de R Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de R Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de R Academy inclui 4 aulas no total.

Por que computação paralela?

Os computadores modernos têm vários núcleos de CPU. Por padrão, o R é executado em um único núcleo, deixando os demais ociosos. O pacote parallel (integrado ao R) permite aproveitar todos os núcleos para acelerar computações repetitivas.

# 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 e stopCluster

makeCluster(n) cria n processos de trabalho. Sempre chame stopCluster(cl) ao terminar para liberar recursos. Uma convenção comum é usar detectCores() - 1 para deixar um núcleo disponível para o sistema operacional.

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: compartilhando variáveis

Os processos de trabalho têm seu próprio espaço de memória e não conseguem acessar seu ambiente global. Use clusterExport(cl, varlist) para copiar objetos nomeados do processo principal para todos os processos de trabalho.

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: executando código de configuração

clusterEvalQ(cl, expr) avalia uma expressão em cada processo de trabalho — é útil para carregar pacotes ou obter arquivos auxiliares em todos os nós antes do início da computação 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 paralelo

parLapply(cl, X, FUN) é o equivalente paralelo de lapply(). Ele distribui os elementos de X entre os processos de trabalho e reúne os resultados em uma lista. Funciona em todas as plataformas (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: resultados simplificados

parSapply(cl, X, FUN) é a versão paralela de sapply(). Ele simplifica automaticamente o resultado para um vetor ou uma matriz quando possível, sendo conveniente quando sua função retorna um valor escalar.

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: paralelismo baseado em bifurcação

mclapply() usa bifurcação — ele copia o processo atual do R em vez de criar novos processos, o que torna sua inicialização mais rápida e dá aos processos de trabalho acesso implícito ao ambiente pai. No entanto, funciona apenas em Unix/macOS, não no 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')
}

Comparando o desempenho sequencial e paralelo

O paralelismo tem custos adicionais: a inicialização do cluster, a serialização de dados e a comunicação entre processos consomem tempo. Ele só compensa quando a computação por elemento é suficientemente cara para superar esses custos.

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)

Definindo a semente de RNG em paralelo

A geração de números aleatórios em paralelo é complexa — cada processo de trabalho precisa de um fluxo independente e reproduzível. Use clusterSetRNGStream(cl, seed) com o gerador L'Ecuyer-CMRG para obter aleatoriedade paralela reproduzível.

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)

Tratamento de erros em clusters

Se um processo de trabalho lançar um erro, parLapply() o relança no processo principal. Envolva as chamadas em tryCatch() dentro da função ou use tryCatch() ao redor de toda a chamada a parLapply() para tratar as falhas de forma adequada.

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)

Padrão prático de paralelismo

Este é um fluxo de trabalho paralelo completo e idiomático: detectar os núcleos, criar o cluster, exportar as dependências, executar a computação, reunir os resultados e sempre interromper o cluster — mesmo que ocorra um erro — usando 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')

Verificação rápida

Qual função você deve usar para garantir que stopCluster(cl) seja chamada mesmo que ocorra um erro no seu código paralelo?

Recapitulação: pacote parallel

Principais conclusões:

  • detectCores() informa os núcleos disponíveis; use detectCores() - 1 para definir o tamanho do cluster
  • makeCluster(n) / stopCluster(cl) gerenciam os ciclos de vida dos processos de trabalho
  • clusterExport() copia objetos; clusterEvalQ() executa código de configuração nos processos de trabalho
  • parLapply() / parSapply() são iteradores paralelos multiplataforma
  • mclapply() funciona apenas em Unix, mas inicia mais rapidamente devido à bifurcação
  • Use on.exit(stopCluster(cl)) para garantir a limpeza
  • O paralelismo só compensa em tarefas computacionalmente pesadas
# 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')
Grátis para começar

Aprenda R com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
43
Aulas
159

Perguntas Frequentes

A aula “Pacote parallel e detectCores()” é grátis?

Sim — o texto completo de “Pacote parallel e detectCores()” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de R Academy, atualize para CoddyKit PRO. O curso de R Academy inclui 4 aulas no total.

O que vou aprender em “Pacote parallel e detectCores()”?

Inicie agrupamentos derivados ou por soquetes e distribua o trabalho entre núcleos de CPU. Você pratica R Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar R Academy?

Nenhuma experiência prévia é necessária. R Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Pacote parallel e detectCores()”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de R Academy?

Sim. Cada aula de R Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Pacote parallel e detectCores()
  2. O framework future
  3. furrr: operações paralelas com purrr
  4. Depuração e balanceamento de carga de código paralelo
← Voltar para R Academy