Целью будущего пакета является предоставление очень простого и равномерного способа оценки выражений R Asynchrony с использованием различных ресурсов, доступных для пользователя.
В программировании будущее - это абстракция для значения , которое может быть доступно в какой -то момент в будущем. Состояние будущего может быть либо неразрешенным , либо разрешено . Как только это будет решено, значение доступно мгновенно. Если значение запрашивается, в то время как будущее все еще не разрешено, текущий процесс блокируется до тех пор, пока будущее не будет разрешено. Можно проверить, разрешено ли будущее или нет без блокировки. Как и когда и когда есть фьючерсы, зависит от того, какая стратегия используется для их оценки. Например, будущее может быть разрешено с использованием последовательной стратегии, что означает, что она разрешена в текущем сеансе R. Другие стратегии могут заключаться в разрешении фьючерсного асинхронного, например, путем оценки выражений параллельно на текущей машине или одновременно на вычислительном кластере.
Вот пример, иллюстрирующий, как работают основы будущего. Во -первых, рассмотрим следующий фрагмент кода, который использует обычный код R:
> v <- {
+ cat( " Hello world! n " )
+ 3.14
+ }
Hello world !
> v
[ 1 ] 3.14
Он работает, присваивая значение выражения переменной v
, а затем мы печатаем значение v
. Более того, когда оценивается выражение для v
мы также печатаем сообщение.
Вот тот же фрагмент кода, измененный для использования фьючерса, вместо этого:
> library( future )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
Разница в том, как v
построен; С Plain R мы используем <-
тогда как с фьючерсами мы используем %<-%
. Другое отличие состоит в том, что вывод реле после того, как будущее разрешается (не во время) и когда значение запрашивается (см. Vignette «Text Text»).
Так почему будущее полезно? Потому что мы можем выбрать оценить будущее выражение в отдельном процессе R, асинхронно, просто переключив настройки как:
> library( future )
> plan( multisession )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
С асинхронным будущим процесс текущего/основного R не блокирует, что означает, что он доступен для дальнейшей обработки, в то время как фьючерсы разрешаются в отдельных процессах, работающих в фоновом режиме. Другими словами, фьючерсы обеспечивают простую, но все же мощную конструкцию для параллельной и / или распределенной обработки в R.
Теперь, если вас нельзя беспокоить, чтобы прочитать все задумчивые подробности о будущем, но просто хотите попробовать их, затем пропустите до конца, чтобы поиграть с демонстрацией Мандельброта, используя как параллельную, так и непараллельную оценку.
Фьючерсы могут быть созданы либо неявно , либо явно . В приведенном выше примере мы использовали неявное будущее, созданное через конструкцию v %<-% { expr }
. Альтернативой является явное будущее с использованием конструкций f <- future({ expr })
и v <- value(f)
. С этими примером можно было бы написать как:
> library( future )
> f <- future({
+ cat( " Hello world! n " )
+ 3.14
+ })
> v <- value( f )
Hello world !
> v
[ 1 ] 3.14
Любой стиль будущей конструкции работает одинаково (*) хорошо. Неявный стиль наиболее похож на то, как написан обычный код R. В принципе, все, что вам нужно сделать, это заменить <-
%<-%
, чтобы превратить задание в будущее задание. С другой стороны, эта простота также может быть обманчивой, особенно когда используется асинхронное будущее. Напротив, явный стиль дает гораздо более ясным, что используется будущее, что снижает риск ошибок и лучше передает дизайн другим, читающим ваш код.
(*) Есть случаи, когда %<-%
нельзя использовать без некоторых (небольших) модификаций. Мы вернемся к этому в разделе «Ограничения при использовании неявного будущего» в конце этого документа.
Подводя итог, для явного будущего мы используем:
f <- future({ expr })
- создает будущееv <- value(f)
- Получает значение будущего (блокирует, если не решен)Для неявного будущего мы используем:
v %<-% { expr }
- создает будущее и обещание ее ценностиЧтобы сделать это просто, мы будем использовать неявный стиль в остальной части этого документа, но все обсуждаемые также применяется к явным будущим.
Будущий пакет реализует следующие виды будущего:
Имя | Ос | Описание |
---|---|---|
синхронно: | не параллельно: | |
sequential | все | последовательно и в текущем процессе R |
асинхронный: | параллель : | |
multisession | все | Фоновые сеансы (на текущей машине) |
multicore | Не Windows/не rstudio | разветвленные r процессы (на текущей машине) |
cluster | все | Внешние сеансы R на текущих, локальных и/или удаленных машинах |
Будущий пакет разработан таким образом, чтобы поддержка дополнительных стратегий также может быть реализована. Например, пакет Future.callr предоставляет будущие бэкэнды, которые оценивают будущее в фоновом процессе R, используя пакет Callr - они работают аналогично Futures multisession
, но имеют несколько преимуществ. Продолжая, пакет Future.batchtools предоставляет будущее для всех типов функций кластера («бэкэнды»), которые поддерживает пакет Batchtools. В частности, фьючерсы для оценки R -выражений с помощью планировщиков заданий, таких как Slurm, Toote/PBS, двигатель Oracle/Sun Grid (SGE) и объект обмена нагрузкой (LSF).
По умолчанию будущие выражения оцениваются с нетерпением (= мгновенно) и синхронно (в текущем сеансе R). Эта стратегия оценки называется «последовательной». В этом разделе мы рассмотрим каждую из этих стратегий и обсудим, что у них общего и как они различаются.
Прежде чем пройти через каждую из различных будущих стратегий, вероятно, полезно прояснить цели будущего API (как определено будущим пакетом). При программировании с фьючерсом не должно иметь значения, какая будущая стратегия используется для выполнения кода. Это потому, что мы не можем действительно знать, какие вычислительные ресурсы имеют доступ к пользователю, поэтому выбор стратегии оценки должен быть в руках пользователя, а не разработчика. Другими словами, код не должен делать никаких предположений о типе используемого будущего, например, синхронно или асинхронного.
Одним из проектов будущего API было инкапсулировать любые различия, так что все виды будущего будут работать одинаково. Это, несмотря на выражения, могут быть оценены локально в текущей сеансе R или по всему миру в отдаленных сеансах R. Еще одним очевидным преимуществом наличия последовательного API и поведения среди различных типов будущего является то, что он помогает при прототипировании. Как правило, можно использовать последовательную оценку при создании сценария, а затем, когда сценарий полностью разработан, можно включить асинхронную обработку.
Из -за этого по умолчанию различных стратегий таковы, что результаты и побочные эффекты оценки будущего выражения максимально одинаковы. Более конкретно, следующее верно для всех будущих:
Вся оценка проводится в локальной среде (то есть local({ expr })
), так что назначения не влияют на вызову. Это естественно при оценке во внешнем процессе R, но также применяется при оценке в текущем сеансе R.
Когда будущее построено, глобальные переменные идентифицируются . Для асинхронной оценки глобалы экспортируются в процесс/сеанс R, который будет оценивать будущее выражение. Для последовательного будущего с ленивой оценкой ( lazy = TRUE
) глобальные жители «заморожены» (клонируются в местную среду будущего). Кроме того, чтобы защитить от экспорта слишком больших объектов по ошибке, существует встроенное утверждение о том, что общий размер всех глобалов меньше, чем заданный порог (контролируется через опцию, См help("future.options")
) Если порог превышен, выброшена информативная ошибка.
Будущие выражения оцениваются только один раз . Как только значение (или ошибка) будет собрано, оно будет доступно для всех последующих запросов.
Вот пример, иллюстрирующий, что все задания выполняются в местной среде:
> plan( sequential )
> a <- 1
> x % <- % {
+ a <- 2
+ 2 * a
+ }
> x
[ 1 ] 4
> a
[ 1 ] 1
Теперь мы готовы изучить различные будущие стратегии.
Синхронное будущее разрешается один за другим и чаще всего в процессе R, который их создает. Когда решается синхронное будущее, оно блокирует основной процесс до разрешения.
Последовательное будущее - это по умолчанию, если не указано иное. Они были разработаны, чтобы вести себя как можно более сходным, чтобы регулярно оценить R, в то же время выполняя будущие API и его поведение. Вот пример, иллюстрирующий их свойства:
> plan( sequential )
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437557
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Поскольку проходит энергичная последовательная оценка, каждое из трех будущих решается мгновенно в тот момент, когда оно создается. Примечание также, как pid
в вызывающей среде, которой был назначен идентификатор процесса текущего процесса, не перезаписывается и не удален. Это потому, что будущее оценивается в местной среде. Поскольку используется синхронная (одно) обработка, будущее b
разрешается основным процессом R (все еще в локальной среде), поэтому ценность b
и pid
одинакова.
Далее мы обратимся к асинхронному будущему, которые являются будущим, которые разрешены на заднем плане. По дизайну эти будущие не блокируют, то есть после создания процесс вызова доступен для других задач, включая создание дополнительного будущего. Только когда процесс вызова пытается получить доступ к значению будущего, которое еще не разрешено, или пытаться создать другое асинхронное будущее, когда все доступные R -процессы заняты обслуживанием других будущих, которые он блокирует.
Мы начинаем с Multisession Futures, потому что они поддерживаются всеми операционными системами. Мультисессионное будущее оценивается в фоновом сеансе R, работающем на той же машине, что и процесс вызова R. Вот наш пример с многосессионной оценкой:
> plan( multisession )
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437616
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Первое, что мы наблюдаем, это то, что значения a
, c
и pid
совпадают с ранее. Тем не менее, мы замечаем, что b
отличается от раньше. Это связано с тем, что будущее b
оценивается в другом процессе R, и поэтому он возвращает другой идентификатор процесса.
Когда используется многосессионная оценка, в пакете запускается набор сеансов R в фоновом режиме, которые будут служить многосессионным фьючерсам, оценивая их выражения по мере их создания. Если все фоновые сеансы заняты обслуживанием других будущих, создание следующего многосессионного будущего заблокировано до тех пор, пока фоновая сессия снова не станет доступной. Общее количество запускаемых фоновых процессов определяется значением availableCores()
, например,
> availableCores()
mc.cores
2
Этот конкретный результат сообщает нам, что опция mc.cores
была установлена так, чтобы нам разрешили использовать в общем двух (2) процессах, включая основной процесс. Другими словами, с этими настройками будет два (2) фоновых процесса, обслуживающих многосессионные фьючерсы. availableCores()
также является гибким для различных вариантов и переменных системной среды. Например, если используются вычислительные планировщики кластеров (например, крутящий момент/PBS и Slurm), они устанавливают конкретную переменную среды, указав количество ядер, которые были выделены для любого данного задания; availableCores()
также признает это. Если ничего другого не указано, все доступные ядра на машине будут использоваться, ср. parallel::detectCores()
. Для получения более подробной информации, пожалуйста, см help("availableCores", package = "parallelly")
.
В операционных системах, где R поддерживает разбрызгивание процессов, которые в основном представляют собой всю операционную систему, кроме Windows, альтернатива нетоловым сеансам R в фоновом режиме состоит в том, чтобы разжечь существующий процесс R. Чтобы использовать многоядерные фьючерсы при поддержке, укажите:
plan( multicore )
Как и для фьючерсов с мультисессией, максимальное количество проводящих параллельных процессов будет определяться availableCores()
, поскольку в обоих случаях оценка проводится на локальной машине.
Сокращение процесса R может быть быстрее, чем работа с отдельным сеансом R, работающим в фоновом режиме. Одна из причин заключается в том, что накладные расходы на экспорт больших глобалов в фоновую сессию могут быть больше, чем при разбите, и, следовательно, используется общая память. С другой стороны, общая память читается только , что означает, что любые модификации общих объектов одним из раздвоенных процессов («работники») приведут к копии операционной системой. Это также может произойти, когда коллектор мусора R работает в одном из раздвоенных процессов.
С другой стороны, разбивание процесса также считается нестабильным в некоторых средах R. Например, при запуске R из Rstudio Process Witking может привести к разбитым сеансам R. Из -за этого будущий пакет отключает многоядерные фьючерсы по умолчанию при запуске из RStudio. Смотрите help("supportsMulticore")
для получения более подробной информации.
Фьючерсы на кластер оценивают выражения на специальном кластере (как реализовано параллельным пакетом). Например, предположим, что у вас есть доступ к трем узлам n1
, n2
и n3
, вы можете использовать их для асинхронной оценки как:
> plan( cluster , workers = c( " n1 " , " n2 " , " n3 " ))
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437715
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Любые типы кластеров, которые создают parallel::makeCluster()
могут использоваться для фьючерсов на кластер. Например, приведенный выше кластер может быть явно настроен как:
cl <- parallel :: makeCluster(c( " n1 " , " n2 " , " n3 " ))
plan( cluster , workers = cl )
Кроме того, считается хорошим стилем, чтобы выключить кластер cl
когда он больше не нужен, то есть на вызов parallel::stopCluster(cl)
. Тем не менее, он выключится, если основной процесс будет прекращен. Для получения дополнительной информации о том, как настроить и управлять такими кластерами, см. help("makeCluster", package = "parallel")
. Кластеры, созданные неявно с использованием plan(cluster, workers = hosts)
, где hosts
являются вектором символов, также будут отключены при завершении основной сеансы R, или когда изменяется будущая стратегия, например, позвонив по plan(sequential)
.
Обратите внимание, что при автоматической настройке аутентификации (например, пары клавиш SSH) ничто не мешает нам использовать тот же подход для использования кластера удаленных машин.
Если вы хотите запустить несколько работников на каждом узле, повторите имя узла столько раз, сколько и количество рабочих для работы на этом узле. Например,
> plan(cluster, workers = c(rep("n1", times = 3), "n2", rep("n3", times = 5)))
Будет управлять тремя работниками на n1
, один на n2
и пять на n3
, в общей сложности девять параллельных работников.
Это далеко мы обсуждали, что можно назвать «плоской топологией» будущего, то есть все будущее создается и назначено в одну и ту же среду. Тем не менее, ничто не помешает нам использовать «вложенную топологию» будущего, где один набор будущих может, в свою очередь, создать еще один набор будущих внутри и так далее.
Например, вот пример двух «лучших» фьючерсов ( a
и b
), в котором используется многосессионная оценка и где второе будущее ( b
) в свою очередь использует два внутренних будущих фьючерса:
> plan( multisession )
> pid <- Sys.getpid()
> a % <- % {
+ cat( " Future 'a' ... n " )
+ Sys.getpid()
+ }
> b % <- % {
+ cat( " Future 'b' ... n " )
+ b1 % <- % {
+ cat( " Future 'b1' ... n " )
+ Sys.getpid()
+ }
+ b2 % <- % {
+ cat( " Future 'b2' ... n " )
+ Sys.getpid()
+ }
+ c( b.pid = Sys.getpid(), b1.pid = b1 , b2.pid = b2 )
+ }
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437804
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437805 1437805 1437805
По мнению идентификатора процесса, мы видим, что в общей сложности есть три различных процесса для разрешения будущего. Существует основной процесс R (PID 1437557), и есть два процесса, используемые a
(PID 1437804) и b
(PID 1437805). Тем не менее, два будущих ( b1
и b2
), которые вложены b
оцениваются с помощью того же R -процесса, что и b
Это связано с тем, что вложенные фьючерсы используют последовательную оценку, если не указано иное. Есть несколько причин для этого, но главная причина в том, что он защищает нас от нереста от большого количества фоновых процессов по ошибке, например, через рекурсивные вызовы.
Чтобы указать топологию оценки другого типа, за исключением первого уровня будущего, разрешенного с помощью многосессионной оценки и второго уровня посредством последовательной оценки, мы можем предоставить список стратегий оценки plan()
. Во -первых, те же стратегии оценки, что и выше, могут быть явно указаны как:
plan( list ( multisession , sequential ))
На самом деле мы получим одно и то же поведение, если мы попробуем с несколькими уровнями многосессионных оценок;
> plan( list ( multisession , multisession ))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437901
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437902 1437902 1437902
Причина этого также состоит в том, чтобы защитить нас от запуска большего количества процессов, чем то, что может поддержать машина. Внутренне это делается путем установки mc.cores = 1
, так что такие функции, как parallel::mclapply()
вернутся, чтобы запустить последовательно. Это относится к многоуровневой оценке.
Продолжая, если мы начнем с последовательной оценки, а затем используем многосессионную оценку для любого вложенного будущего, мы получаем:
> plan( list ( sequential , multisession ))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437557
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437557 1438017 1438016
которые ясно показывают, что a
и b
разрешены в процессе вызова (PID 1437557), тогда как два вложенных фьючерса ( b1
и b2
) разрешаются в двух отдельных R -процессах (PIDS 1438017 и 1438016).
Сказав это, действительно возможно использовать вложенные стратегии оценки многосессии, если мы явно указали (прочитать силу ) количество ядер, доступных на каждом уровне. Для этого нам нужно «настраивать» настройки по умолчанию, которые можно сделать следующим образом:
> plan( list (tweak( multisession , workers = 2 ), tweak( multisession ,
+ workers = 2 )))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1438105
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1438106 1438211 1438212
Во -первых, мы видим, что как a
, так и b
разрешаются в разных процессах (PIDS 1438105 и 1438106), чем в процессе вызова (PID 1437557). Во -вторых, два вложенных фьючерса ( b1
и b2
) разрешены в еще двух других R -процессах (PIDS 1438211 и 1438212).
Более подробную информацию о работе с вложенным фьючерсом и различными стратегиями оценки на каждом уровне см. В виньетке «Futures в R: будущие топологии».
Можно проверить, было ли будущее разрешено или нет без блокировки. Это может быть сделано с использованием функции resolved(f)
, которая принимает явное будущее f
в качестве входных данных. Если мы работаем с неявным будущим (как во всех примерах выше), мы можем использовать функцию f <- futureOf(a)
для извлечения явного будущего из неявного. Например,
> plan( multisession )
> a % <- % {
+ cat( " Future 'a' ... " )
+ Sys.sleep( 2 )
+ cat( " done n " )
+ Sys.getpid()
+ }
> cat( " Waiting for 'a' to be resolved ... n " )
Waiting for ' a ' to be resolved ...
> f <- futureOf( a )
> count <- 1
> while ( ! resolved( f )) {
+ cat( count , " n " )
+ Sys.sleep( 0.2 )
+ count <- count + 1
+ }
1
2
3
4
5
6
7
8
9
10
> cat( " Waiting for 'a' to be resolved ... DONE n " )
Waiting for ' a ' to be resolved ... DONE
> a
Future ' a ' ... done
[ 1 ] 1438287
Иногда будущее - это не то, что вы ожидали. Если при оценке будущего возникает ошибка, ошибка распространяется и бросается в качестве ошибки в вызывающей среде при запросе будущего значения . Например, если мы используем ленивую оценку в будущем, которая генерирует ошибку, мы могли бы увидеть что -то вроде
> plan( sequential )
> b <- " hello "
> a % <- % {
+ cat( " Future 'a' ... n " )
+ log( b )
+ } % lazy % TRUE
> cat( " Everything is still ok although we have created a future that will fail. n " )
Everything is still ok although we have created a future that will fail.
> a
Future ' a ' ...
Error in log( b ) : non - numeric argument to mathematical function
Ошибка бросается каждый раз, когда запрашивается значение, то есть, если мы попытаемся снова получить значение, будет генерировать ту же ошибку (и вывод):
> a
Future ' a ' ...
Error in log( b ) : non - numeric argument to mathematical function
In addition : Warning message :
restarting interrupted promise evaluation
Чтобы увидеть последний вызов в стеке вызовов, который дал ошибку, мы можем использовать функцию backtrace()
в будущем, т.е.
> backtrace( a )
[[ 1 ]]
log( a )
(*) Обычно используемый traceback()
не предоставляет соответствующую информацию в контексте будущего. Кроме того, к сожалению, невозможно увидеть список вызовов (оцениваемых выражений), которые привели к ошибке; Только вызов, который дал ошибку (это связано с ограничением в tryCatch()
используемом внутри).
Всякий раз, когда экспрессия R следует оценить асинхронно (параллельно) или последовательно посредством ленивой оценки, глобальные (известные как «свободные») объекты должны быть идентифицированы и переданы оценщику. Они должны быть переданы именно так, как и в то время, когда было создано будущее, потому что для ленивой оценки глобальные убытки могут измениться между тем, когда оно создается, и когда оно разрешено. Для асинхронной обработки, причина, по которой глобальные исчисления должны быть идентифицированы, заключается в том, что они могут быть экспортированы в процесс, который оценивает будущее.
Будущий пакет пытается автоматизировать эти задачи как можно дальше. Это происходит с помощью пакета Globals, который использует проверку статического кода для идентификации глобальных переменных. Если глобальная переменная идентифицирована, она захвачена и доступна для процесса оценки. Более того, если глобальный определяется в пакете, то этот глобальный не экспортируется. Вместо этого убедится, что соответствующий пакет прикреплен при оценке будущего. Это не только лучше отражает настройку основного сеанса R, но также минимизирует необходимость экспорта глобалов, что экономит не только память, но и время и пропускную способность, особенно при использовании удаленных вычислительных узлов.
Наконец, следует уточнить, что выявление глобалов из статического проверки кода является сложной проблемой. Всегда будут угловые случаи, когда автоматическая идентификация глобалов не сбои, так что либо ложные глобальные, выявленные (меньше заботы), либо некоторые из истинных глобалов отсутствуют (что приведет к ошибке времени выполнения или, возможно, неверным результатам). Фьючерсы на Vignette в R: общие проблемы с решениями «приведены примеры общих случаев и объясняют, как их избежать, а также как помочь пакету идентифицировать глобальных значений или игнорировать ложно идентифицированные глобалы. Если этого недостаточно, всегда можно вручную указать глобальные переменные по их именам (например, globals = c("a", "slow_sum")
) или в виде пар именных значений (например, globals = list(a = 42, slow_sum = my_sum)
).
Существует одно ограничение с неявным будущим, которое не существует для явных. Потому что явное будущее, как и любой другой объект в R, его можно назначить в любом месте/на что угодно. Например, мы можем создать несколько из них в цикле и назначить их в список, например,
> plan( multisession )
> f <- list ()
> for ( ii in 1 : 3 ) {
+ f [[ ii ]] <- future({
+ Sys.getpid()
+ })
+ }
> v <- lapply( f , FUN = value )
> str( v )
List of 3
$ : int 1438377
$ : int 1438378
$ : int 1438377
Это невозможно сделать при использовании неявного будущего. Это связано с тем, что оператор назначения %<-%
не может быть использован во всех случаях, когда можно использовать обычный оператор <-
назначения. Он может использоваться только для назначения будущих значений средам (включая среду вызывающего оборота), так же, как работает assign(name, value, envir)
. Тем не менее, мы можем назначить неявное будущее средам с использованием названных индексов , например,
> plan( multisession )
> v <- new.env()
> for ( name in c( " a " , " b " , " c " )) {
+ v [[ name ]] % <- % {
+ Sys.getpid()
+ }
+ }
> v <- as.list( v )
> str( v )
List of 3
$ a : int 1438485
$ b : int 1438486
$ c : int 1438485
Здесь as.list(v)
блокируется до тех пор, пока v
будут разрешены все будущие в окружающей среде. Затем их значения собираются и возвращаются в качестве обычного списка.
Если требуются числовые индексы , можно использовать среды списка . Среда списков, которые реализованы пакетом Listenv, являются обычными средами с индивидуальными операторами подмножества, позволяющие индексировать их так же, как в списках, которые можно проиндексировать. Используя среды списков, в которых мы иначе используем списки, мы также можем назначить неявное будущее на списки, подобные объектам, используя числовые индексы. Например,
> library( listenv )
> plan( multisession )
> v <- listenv()
> for ( ii in 1 : 3 ) {
+ v [[ ii ]] % <- % {
+ Sys.getpid()
+ }
+ }
> v <- as.list( v )
> str( v )
List of 3
$ : int 1438582
$ : int 1438583
$ : int 1438582
Как и ранее, as.list(v)
блокирует, пока все будущие не будут разрешены.
Чтобы увидеть живую иллюстрацию, как оцениваются различные виды будущих, запустите демонстрацию Мандельброта этого пакета. Сначала попробуйте последовательную оценку,
library( future )
plan( sequential )
demo( " mandelbrot " , package = " future " , ask = FALSE )
что напоминает, как будет работать сценарий, если будущий фьючерс не будет использован. Затем попробуйте MultiSession Evaluation, которая вычисляет различные плоскости Мандельброта, используя параллельные R -процессы, работающие на фоне. Пытаться,
plan( multisession )
demo( " mandelbrot " , package = " future " , ask = FALSE )
Наконец, если у вас есть доступ к нескольким машинах, вы можете попытаться настроить группу работников и использовать их, например,
plan( cluster , workers = c( " n2 " , " n5 " , " n6 " , " n6 " , " n9 " ))
demo( " mandelbrot " , package = " future " , ask = FALSE )
R пакет будущий доступен на CRAN и может быть установлен в R как:
install.packages( " future " )
Чтобы установить предварительную версию, которая доступна в Git Branch, develop
на GitHub, используйте:
remotes :: install_github( " futureverse/future " , ref = " develop " )
Это установит пакет из источника.
Чтобы внести свой вклад в этот пакет, см. Appling.md.