Фреймворк future
Используйте plan(multisession) и future() для асинхронных переносимых параллельных вычислений
«Фреймворк future» — бесплатный урок R Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.
Что такое пакет future
Пакет future предоставляет единый высокоуровневый API для асинхронного и параллельного программирования в R. Future — это заполнитель для значения, которое станет доступно позже и, возможно, будет вычислено на другом ядре или компьютере.
library(future)
# A simple future: computation happens asynchronously
f <- future({
Sys.sleep(0.5) # simulate slow work
42
})
cat('Future created, doing other work...\n')
# Retrieve the result (blocks until done)
result <- value(f)
cat('Result:', result, '\n')plan(): выбор обработчика
plan() задаёт стратегию выполнения для всех последующих future. Наиболее распространённые стратегии: sequential (по умолчанию, один поток), multisession (несколько сеансов R, работает везде) и multicore (создание процессов методом fork, только Unix/macOS).
library(future)
# Default: sequential (no parallelism)
plan(sequential)
cat('Strategy:', class(plan())[1], '\n')
# Parallel with separate R sessions (works on Windows too)
plan(multisession, workers = 4)
cat('Strategy:', class(plan())[1], '\n')
# Forking (Unix/macOS only, lower overhead)
if (.Platform$OS.type != 'windows') {
plan(multicore, workers = 4)
cat('Strategy:', class(plan())[1], '\n')
}
# Reset to sequential
plan(sequential)value(): ожидание результатов
value(f) блокирует текущий процесс до завершения вычисления future, а затем возвращает результат. Если в future возникла ошибка, value() повторно вызывает её в вызывающем сеансе.
library(future)
plan(multisession, workers = 2)
# Launch two slow tasks concurrently
f1 <- future({ Sys.sleep(0.3); sum(1:1000) })
f2 <- future({ Sys.sleep(0.3); prod(1:10) })
# Both run in parallel; total time ~ 0.3s not 0.6s
start <- proc.time()[['elapsed']]
v1 <- value(f1)
v2 <- value(f2)
elapsed <- proc.time()[['elapsed']] - start
cat('v1:', v1, ' v2:', v2, '\n')
cat('Elapsed:', round(elapsed, 2), 's\n')
plan(sequential)Оператор %<-%
Оператор %<-% — это синтаксический сахар для future() и ленивого вызова value(). Он выглядит как обычное присваивание, но правая часть выполняется асинхронно. Значение извлекается при первом обращении к переменной.
library(future)
plan(multisession, workers = 2)
# %<-% starts the computation immediately in the background
x %<-% {
Sys.sleep(0.3)
rnorm(5, mean = 10)
}
y %<-% {
Sys.sleep(0.3)
runif(5, min = 1, max = 5)
}
cat('Both running in background...\n')
# Accessing x or y here blocks until ready
cat('x:', round(x, 2), '\n')
cat('y:', round(y, 2), '\n')
plan(sequential)Автоматическое обнаружение глобальных переменных
Одна из самых мощных возможностей future — обнаружение глобальных переменных: пакет автоматически находит переменные, на которые ссылается код future в вызывающем окружении, и отправляет их рабочему процессу — ручной вызов clusterExport() не требуется.
library(future)
plan(multisession, workers = 2)
# These globals are auto-detected and sent to the worker
threshold <- 50
multiplier <- 3
f <- future({
x <- threshold * multiplier # uses both globals
x + 1
})
cat('Result:', value(f), '\n') # 151
# Inspect which globals were identified
future_obj <- future({
threshold + multiplier
}, lazy = TRUE)
cat('Globals found:', paste(names(future::getGlobalsAndPackages(future_obj)$globals), collapse = ', '), '\n')
plan(sequential)future_options: настройка поведения
future.options (доступные через аргументы options() или future::plan()) управляют глобальными параметрами: максимальным объёмом памяти для одной future, стратегией обнаружения глобальных переменных и поведением при превышении времени ожидания.
library(future)
# Cap memory per future at 500 MB
options(future.globals.maxSize = 500 * 1024^2) # bytes
# Disable automatic global detection (manual control)
options(future.globals.onReference = 'error')
# Inspect current settings
cat('Max globals size (MB):',
getOption('future.globals.maxSize') / 1024^2, '\n')
# Restore defaults
options(future.globals.maxSize = 500 * 1024^2)
options(future.globals.onReference = NULL)
plan(multisession, workers = 2)
# Large object: you'd get an error if it exceeds the cap
big_vec <- 1:1e5 # small enough
f <- future(sum(big_vec))
cat('Sum:', value(f), '\n')
plan(sequential)Вложенные future
Future могут быть вложенными: future может сама создавать внутренние future. По умолчанию внутренние future выполняются последовательно, если не задать вложенный план с помощью plan(list(...)). Это удобно для двухуровневого параллелизма.
library(future)
# Two-level parallelism: outer uses multisession, inner sequential
plan(list(multisession, sequential))
outer_futures <- lapply(1:3, function(i) {
future({
# Each outer worker runs its own sequential inner work
inner <- lapply(1:4, function(j) i * j)
unlist(inner)
})
})
results <- lapply(outer_futures, value)
for (i in seq_along(results)) {
cat('Outer', i, ':', results[[i]], '\n')
}
plan(sequential)Обработка ошибок в future
Если при вычислении future возникает ошибка, value() повторно вызывает её. Оберните value() в tryCatch(), чтобы обрабатывать ошибки отдельных future и не останавливать весь конвейер.
library(future)
plan(multisession, workers = 2)
# This future will error
bad_future <- future(log(-1, base = 'oops')) # invalid arg
good_future <- future(sqrt(144))
# Handle errors gracefully
bad_result <- tryCatch(
value(bad_future),
error = function(e) {
cat('Caught error:', conditionMessage(e), '\n')
NA
}
)
good_result <- value(good_future)
cat('bad_result:', bad_result, '\n')
cat('good_result:', good_result, '\n')
plan(sequential)Проверка состояния future
resolved(f) возвращает TRUE, если future завершилась, не блокируя выполнение. Это позволяет проверять завершение и выполнять полезную работу во время ожидания, создавая цикл неблокирующей проверки.
library(future)
plan(multisession, workers = 2)
f <- future({
Sys.sleep(0.5)
'done'
})
# Poll without blocking
counter <- 0
while (!resolved(f)) {
counter <- counter + 1
Sys.sleep(0.1)
cat('Polling... (', counter, ')\n')
}
cat('Future resolved! Result:', value(f), '\n')
cat('Polled', counter, 'times\n')
plan(sequential)Объединение future с lapply
Объедините lapply() с future(), чтобы распределить N задач, а lapply() с value() — чтобы собрать результаты. Этот шаблон даёт явный контроль над моментом запуска каждой future.
library(future)
plan(multisession, workers = 4)
# Fan out: create all futures
params <- list(
list(n = 1000, mean = 0),
list(n = 1000, mean = 5),
list(n = 1000, mean = 10),
list(n = 1000, mean = -3)
)
futures <- lapply(params, function(p) {
future({
x <- rnorm(p$n, mean = p$mean)
list(mean = mean(x), sd = sd(x))
})
})
# Fan in: collect results
results <- lapply(futures, value)
for (i in seq_along(results)) {
cat('Param mean', params[[i]]$mean,
'-> observed mean:', round(results[[i]]$mean, 3), '\n')
}
plan(sequential)Практический пример: оценка сгибов перекрёстной проверки
Перекрёстная проверка — это задача, которая отлично подходит для параллельного выполнения: каждый сгиб независим. С помощью future можно одновременно оценить все K сгибов и существенно сократить реальное время выполнения вычислительно дорогих моделей.
library(future)
plan(multisession, workers = 4)
set.seed(42)
data <- data.frame(x = rnorm(200), y = rnorm(200))
fold_ids <- sample(rep(1:4, 50))
# Launch all folds in parallel
fold_futures <- lapply(1:4, function(k) {
future({
train <- data[fold_ids != k, ]
test <- data[fold_ids == k, ]
fit <- lm(y ~ x, data = train)
preds <- predict(fit, newdata = test)
rmse <- sqrt(mean((test$y - preds)^2))
rmse
})
})
rmses <- unlist(lapply(fold_futures, value))
cat('Fold RMSEs:', round(rmses, 4), '\n')
cat('Mean RMSE:', round(mean(rmses), 4), '\n')
plan(sequential)Быстрая проверка
Вам нужно запустить параллельный код, работающий и в Windows, и в macOS. Какую стратегию plan() следует выбрать?
Итоги: платформа future
Главное:
future({expr})асинхронно выполняет выражение, аvalue(f)извлекает результат%<-%— это синтаксический сахар: присваивание с ленивым вычислением future- Используйте
plan(multisession)для разных платформ иplan(multicore)для создания процессов методом fork в Unix/macOS - Глобальные переменные обнаруживаются автоматически и отправляются рабочим процессам —
clusterExport()не требуется resolved(f)проверяет завершение без блокировки- Оберните
value()вtryCatch(), чтобы конвейеры устойчиво обрабатывали ошибки - После завершения всегда вызывайте
plan(sequential)илиplan()для сброса настроек
library(future)
plan(multisession, workers = 2)
# Concise pattern: fan-out then fan-in
task <- function(i) future({ i^2 + i })
results <- lapply(1:6, task)
cat(unlist(lapply(results, value)), '\n') # 2 6 12 20 30 42
plan(sequential)Часто задаваемые вопросы
Урок «Фреймворк future» бесплатный?
Да — полный текст урока «Фреймворк future» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.
Чему я научусь в уроке «Фреймворк future»?
Используйте plan(multisession) и future() для асинхронных переносимых параллельных вычислений Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать R Academy?
Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Фреймворк future»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке R Academy?
Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Пакет parallel и detectCores()
- Фреймворк future
- furrr: параллельные операции purrr
- Отладка и балансировка нагрузки параллельного кода