R Academy · 강의

furrr: 병렬 purrr 연산

map()을 future_map()으로 손쉽게 바꾸어 즉시 병렬화합니다.

레슨 3/413개 단계

furrr: 병렬 purrr 연산은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

furrr: 병렬 purrr

furrr(future + purrr)는 모든 purrr::map_*() 함수에 바로 바꿔 사용할 수 있는 병렬 대체 함수를 제공합니다. plan()을 설정한 뒤 map()을 future_map()으로 바꾸기만 하면 구조를 전혀 변경하지 않고 파이프라인을 병렬로 실행할 수 있습니다.

library(furrr)
library(future)

# Set up parallel workers
plan(multisession, workers = 4)

# Sequential (purrr)
# result <- purrr::map(1:8, ~.x^2)

# Parallel (furrr) — identical API
result <- future_map(1:8, ~.x^2)
cat(unlist(result), '\n')  # 1 4 9 16 25 36 49 64

plan(sequential)

plan(multisession, workers = 4)

plan()에서 workers를 명시하면 병렬로 실행할 R 세션 수의 상한을 설정할 수 있습니다. CPU를 많이 사용하는 작업에서는 workers = parallel::detectCores() - 1을 사용하여 OS에 코어 하나를 남겨 두는 방식이 일반적입니다.

library(furrr)
library(future)
library(parallel)

# Explicit worker count
n_workers <- max(1, detectCores() - 1)
plan(multisession, workers = n_workers)

cat('Active workers:', nbrOfWorkers(), '\n')
cat('Strategy:', class(plan())[1], '\n')

# Run a simple parallel task
results <- future_map_dbl(1:8, ~sqrt(.x))
cat(round(results, 3), '\n')

plan(sequential)

future_map_dbl 및 형식 지정 변형

purrr와 마찬가지로 furrr는 형식 지정 변형인 future_map_dbl(), future_map_int(), future_map_chr(), future_map_lgl()을 제공합니다. 이 함수들은 반환 형식을 강제하고 목록 대신 원자 벡터를 반환합니다.

library(furrr)
plan(multisession, workers = 2)

# Returns a numeric vector
square_roots <- future_map_dbl(1:6, ~sqrt(.x))
cat('dbl:', round(square_roots, 3), '\n')

# Returns an integer vector
counts <- future_map_int(list('hello', 'world', 'R'), nchar)
cat('int:', counts, '\n')

# Returns a character vector
formatted <- future_map_chr(c(1.23, 4.56, 7.89), ~sprintf('%.1f', .x))
cat('chr:', formatted, '\n')

# Returns a logical vector
positive <- future_map_lgl(-3:3, ~.x > 0)
cat('lgl:', positive, '\n')

plan(sequential)

future_map2: 두 입력 매핑

future_map2(.x, .y, .f)는 두 목록 또는 벡터를 병렬로 순회하면서 대응하는 쌍을 함수에 전달합니다. 이는 purrr::map2()의 병렬 버전입니다.

library(furrr)
plan(multisession, workers = 2)

# Simulate different sample sizes and means
sizes <- c(100, 200, 300, 400)
means <- c(0, 5, -3, 10)

# future_map2 passes each (n, mu) pair to rnorm
samples <- future_map2(sizes, means, ~rnorm(.x, mean = .y))

# Verify: each element has the expected length and approximate mean
for (i in seq_along(samples)) {
  cat('n=', sizes[i], 'target_mean=', means[i],
      'observed_mean=', round(mean(samples[[i]]), 2), '\n')
}

plan(sequential)

furrr_options: 동작 제어

furrr_options()는 모든 future_map_*() 호출에서 .options 인수로 전달합니다. 가장 중요한 설정은 seed = TRUE로, 재현 가능한 난수를 위해 작업자 전체에서 L'Ecuyer-CMRG 병렬 RNG를 활성화합니다.

library(furrr)
plan(multisession, workers = 2)

# Without seed: results differ each run
r1 <- future_map_dbl(1:4, ~rnorm(1))
r2 <- future_map_dbl(1:4, ~rnorm(1))
cat('Without seed - same?', identical(r1, r2), '\n')

# With seed: reproducible
opts <- furrr_options(seed = 42L)
r3 <- future_map_dbl(1:4, ~rnorm(1), .options = opts)
r4 <- future_map_dbl(1:4, ~rnorm(1), .options = opts)
cat('With seed - same?', identical(r3, r4), '\n')
cat('r3:', round(r3, 4), '\n')

plan(sequential)

progressr를 사용한 진행 상황 표시

progressr 패키지는 furrr와 통합되어 병렬 실행 중 진행률 표시줄을 보여 줍니다. 코드를 with_progress()로 감싸고, 매핑되는 함수 안에서 progressor()를 생성하십시오.

library(furrr)
library(progressr)
plan(multisession, workers = 2)

# Enable progress reporting
handlers(global = TRUE)  # show progress in console

with_progress({
  p <- progressor(steps = 8)

  results <- future_map(1:8, function(i) {
    p()  # increment the progress bar
    Sys.sleep(0.1)
    i^2
  })
})

cat('Results:', unlist(results), '\n')

plan(sequential)

furrr의 전역 변수

future 패키지와 마찬가지로 furrr는 .f 내부에서 참조되는 전역 변수를 자동으로 감지합니다. furrr_options(globals = c('var1', 'var2'))를 사용하면 이를 재정의하여 전송할 전역 변수를 정확히 지정할 수 있으므로, 큰 환경을 처리할 때 오버헤드를 줄일 수 있습니다.

library(furrr)
plan(multisession, workers = 2)

# Global variables auto-detected
scale_factor <- 10
offset <- 5

result <- future_map_dbl(
  1:6,
  function(x) x * scale_factor + offset
)
cat(result, '\n')  # 15 25 35 45 55 65

# Explicit globals control
opts <- furrr_options(
  globals = c('scale_factor', 'offset'),
  seed = FALSE
)
result2 <- future_map_dbl(
  1:6,
  function(x) x * scale_factor + offset,
  .options = opts
)
cat('Manual globals:', result2, '\n')

plan(sequential)

future_pmap: 여러 인수 매핑

future_pmap(.l, .f)는 purrr::pmap()의 병렬 버전입니다. 벡터 또는 목록으로 이루어진 목록을 받아 대응하는 행을 이름이 지정된 인수로 전달하므로, 여러 매개변수 조합에 대한 계산을 병렬로 수행할 수 있습니다.

library(furrr)
plan(multisession, workers = 2)

# Parameter grid
params <- list(
  n    = c(50, 100, 150, 200),
  mean = c(0, 1, 2, 3),
  sd   = c(1, 2, 1, 0.5)
)

# future_pmap passes each row as arguments to rnorm
samples <- future_pmap(params, function(n, mean, sd) {
  x <- rnorm(n, mean = mean, sd = sd)
  c(obs_mean = round(mean(x), 3), obs_sd = round(sd(x), 3))
})

for (i in seq_along(samples)) {
  cat('n=', params$n[i], ':', samples[[i]], '\n')
}

plan(sequential)

furrr와 purrr 성능 비교

병렬화의 효과는 작업의 무게에 따라 달라집니다. (x^2처럼) 단순한 작업에서는 오버헤드가 지배적이므로 순차 실행이 더 빠릅니다. 많은 모델을 적합하는 것처럼 작업량이 큰 경우에는 병렬화로 상당한 시간을 절약할 수 있습니다.

library(furrr)
library(purrr)
plan(multisession, workers = 4)

# Heavy task: bootstrap a linear model 100 times
heavy <- function(i) {
  n <- 200
  df <- data.frame(x = rnorm(n), y = rnorm(n))
  coef(lm(y ~ x, data = df))[['x']]
}

seq_time <- system.time(map_dbl(1:40, heavy))[['elapsed']]
par_time <- system.time(
  future_map_dbl(1:40, heavy, .options = furrr_options(seed = TRUE))
)[['elapsed']]

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

plan(sequential)

future_map의 오류 처리

요소 중 하나의 계산에서 오류가 발생하면 future_map()은 실행을 중단하고 오류를 다시 발생시킵니다. 오류가 발생해도 계속 진행하려면 함수 주위에 purrr::safely() 또는 purrr::possibly() 래퍼를 사용하십시오.

library(furrr)
library(purrr)
plan(multisession, workers = 2)

# Wrap with safely() to capture errors as results
safe_log <- safely(log, otherwise = NA_real_)

inputs <- list(10, -1, 100, 'text', 0.5)
results <- future_map(inputs, safe_log)

for (i in seq_along(results)) {
  if (is.null(results[[i]]$error)) {
    cat('Input', i, '-> result:', round(results[[i]]$result, 4), '\n')
  } else {
    cat('Input', i, '-> error:', conditionMessage(results[[i]]$error), '\n')
  }
}

plan(sequential)

실용적인 furrr 파이프라인

다음은 데이터 불러오기, 재현 가능한 seed를 사용한 여러 모델의 병렬 적합, 성능 지표 추출, 최적 모델 선택을 모두 furrr 방식으로 수행하는 처음부터 끝까지의 완전한 파이프라인입니다.

library(furrr)
library(purrr)
plan(multisession, workers = 4)

set.seed(1)
n <- 300
df <- data.frame(
  x1 = rnorm(n), x2 = rnorm(n), x3 = rnorm(n),
  y  = rnorm(n)
)

formulas <- list(
  y ~ x1,
  y ~ x1 + x2,
  y ~ x1 + x2 + x3,
  y ~ x1 * x2
)

# Fit all models in parallel
models <- future_map(
  formulas,
  ~lm(.x, data = df),
  .options = furrr_options(seed = FALSE)
)

# Extract adjusted R-squared
adj_r2 <- map_dbl(models, ~summary(.x)$adj.r.squared)
cat('Adjusted R2 per model:',
    paste(round(adj_r2, 4), collapse = ', '), '\n')
cat('Best model:', which.max(adj_r2), '\n')

plan(sequential)

빠른 확인

병렬 future_map() 호출에서 재현 가능한 난수를 사용하려고 합니다. 이를 가능하게 하는 furrr_options() 설정은 무엇인가요?

복습: furrr 패키지

핵심 요점:

  • furrr는 purrr를 바로 바꿔 사용할 수 있는 병렬 대체 패키지입니다. map을 future_map으로 바꾸기만 하면 됩니다
  • 먼저 plan(multisession, workers = n)으로 백엔드를 설정하십시오
  • 형식 지정 변형: future_map_dbl(), future_map_int(), future_map_chr()
  • 여러 입력을 병렬로 매핑하려면 future_map2()와 future_pmap()을 사용합니다
  • 재현 가능한 병렬 RNG에는 furrr_options(seed = 42L)을 사용합니다
  • 오래 걸리는 병렬 작업에서 진행률 표시줄을 사용하려면 progressr를 통합하십시오
  • 오류에 강한 파이프라인을 만들려면 future_map() 내부에서 purrr::safely()를 사용하십시오
library(furrr)
plan(multisession, workers = 2)

results <- future_map_dbl(
  1:6,
  ~.x^2 + sqrt(.x),
  .options = furrr_options(seed = TRUE)
)
cat(round(results, 3), '\n')

plan(sequential)
무료로 시작

AI 튜터와 함께 R을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
43
레슨
159

자주 묻는 질문

“furrr: 병렬 purrr 연산” 강의는 무료인가요?

네 — “furrr: 병렬 purrr 연산” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“furrr: 병렬 purrr 연산”에서 뭘 배우나요?

map()을 future_map()으로 손쉽게 바꾸어 즉시 병렬화합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“furrr: 병렬 purrr 연산” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. parallel 패키지와 detectCores()
  2. future 프레임워크
  3. furrr: 병렬 purrr 연산
  4. 병렬 코드 디버깅과 부하 분산
← R Academy(으)로 돌아가기