parallel 패키지와 detectCores()
포크 또는 소켓 클러스터를 시작하고 CPU 코어 전체에 작업을 분산합니다.
parallel 패키지와 detectCores()은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
병렬 컴퓨팅이 필요한 이유
최신 컴퓨터에는 여러 CPU 코어가 있습니다. 기본적으로 R은 하나의 코어에서 실행되므로 나머지 코어는 유휴 상태로 남습니다. R에 내장된 parallel 패키지를 사용하면 모든 코어를 활용하여 반복적인 계산을 더 빠르게 수행할 수 있습니다.
# 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 및 stopCluster
makeCluster(n)은 n개의 작업자 프로세스를 생성합니다. 리소스를 해제하려면 작업이 끝난 후 항상 stopCluster(cl)을 호출하세요. 일반적으로 OS에 코어 하나를 남겨 두기 위해 detectCores() - 1을 사용합니다.
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: 변수 공유
작업자 프로세스는 자체 메모리 공간을 가지므로 전역 환경을 볼 수 없습니다. clusterExport(cl, varlist)를 사용하면 이름이 지정된 객체를 마스터에서 모든 작업자로 복사할 수 있습니다.
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: 설정 코드 실행
clusterEvalQ(cl, expr)는 모든 작업자에서 표현식을 평가합니다. 주 계산을 시작하기 전에 모든 노드에서 패키지를 로드하거나 도우미 파일을 불러올 때 유용합니다.
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
parLapply(cl, X, FUN)은 lapply()에 해당하는 병렬 함수입니다. X의 요소를 작업자들에게 분배하고 결과를 목록으로 수집합니다. 모든 플랫폼(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: 단순화된 결과
parSapply(cl, X, FUN)은 sapply()의 병렬 버전입니다. 가능한 경우 결과를 벡터 또는 행렬로 자동 단순화하므로 함수가 스칼라를 반환할 때 편리합니다.
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: 포크 기반 병렬 처리
mclapply()는 포크 방식을 사용합니다. 새 프로세스를 생성하는 대신 현재 R 프로세스를 복사하므로 시작이 더 빠르고 작업자가 부모 환경에 암묵적으로 접근할 수 있습니다. 하지만 Unix/macOS에서만 작동하며 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')
}순차 처리와 병렬 처리의 성능 비교
병렬 처리에는 오버헤드가 있습니다. 클러스터 시작, 데이터 직렬화, 프로세스 간 통신에 모두 시간이 걸립니다. 요소별 계산 비용이 이 오버헤드를 상쇄할 만큼 클 때만 효과가 있습니다.
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)병렬 처리에서 RNG 시드 설정
병렬 환경에서 난수를 생성하는 일은 까다롭습니다. 각 작업자에게 독립적이고 재현 가능한 스트림이 필요하기 때문입니다. L'Ecuyer-CMRG 생성기와 함께 clusterSetRNGStream(cl, seed)를 사용하면 재현 가능한 병렬 난수 생성을 얻을 수 있습니다.
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)클러스터의 오류 처리
작업자가 오류를 발생시키면 parLapply()가 해당 오류를 마스터 프로세스에서 다시 발생시킵니다. 함수 내부에서 호출을 tryCatch()로 감싸거나, 전체 parLapply() 호출을 tryCatch()로 감싸서 오류를 적절히 처리하세요.
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)실용적인 병렬 처리 패턴
다음은 관용적인 병렬 처리 작업 흐름 전체의 예입니다. 코어를 확인하고, 클러스터를 만들고, 종속 항목을 내보내고, 계산을 실행하고, 결과를 수집한 다음, 오류가 발생하더라도 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')빠른 확인
병렬 코드 내부에서 오류가 발생하더라도 stopCluster(cl)이 호출되도록 보장하려면 어떤 함수를 사용해야 합니까?
복습: parallel 패키지
핵심 요점:
detectCores()는 사용 가능한 코어 수를 보고합니다. 클러스터 크기에는detectCores() - 1을 사용하세요.makeCluster(n)/stopCluster(cl)은 작업자의 수명 주기를 관리합니다.clusterExport()는 객체를 복사하고,clusterEvalQ()는 작업자에서 설정 코드를 실행합니다.parLapply()/parSapply()는 여러 플랫폼에서 작동하는 병렬 반복 함수입니다.mclapply()는 Unix 전용이지만 포크 방식 덕분에 시작이 더 빠릅니다.- 정리를 보장하려면
on.exit(stopCluster(cl))을 사용하세요. - 병렬 처리는 계산량이 많은 작업에서만 효과가 있습니다.
# 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')자주 묻는 질문
“parallel 패키지와 detectCores()” 강의는 무료인가요?
네 — “parallel 패키지와 detectCores()” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“parallel 패키지와 detectCores()”에서 뭘 배우나요?
포크 또는 소켓 클러스터를 시작하고 CPU 코어 전체에 작업을 분산합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
R Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“parallel 패키지와 detectCores()” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- parallel 패키지와 detectCores()
- future 프레임워크
- furrr: 병렬 purrr 연산
- 병렬 코드 디버깅과 부하 분산