Я думаю, что очень важно разрешить пользователю регистрировать свои собственные параллельные бэкэнд. Бэкэнт doParallel
очень портативен, но что, если они хотят запустить вашу функцию на нескольких узлах кластера? Что делать, если они хотят установить опцию «outfile» makeCluster
? Прискорбно, что прозрачность параллельной поддержки также делает ее бесполезной для многих ваших пользователей.
Я предлагаю вам использовать функцию getDoParRegistered
, чтобы узнать, уже ли пользователь зарегистрировал параллельный бэкэнд, и только зарегистрируйте их для них, если они этого не сделали.
Вот пример:
library(doParallel)
parfun <- function(n=10, parallel=FALSE,
cores=getOption('mc.cores', 2L)) {
if (parallel) {
# honor registration made by user, and only create and register
# our own cluster object once
if (! getDoParRegistered()) {
cl <- makePSOCKcluster(cores)
registerDoParallel(cl)
message('Registered doParallel with ',
cores, ' workers')
} else {
message('Using ', getDoParName(), ' with ',
getDoParWorkers(), ' workers')
}
`%d%` <- `%dopar%`
} else {
message('Executing parfun sequentially')
`%d%` <- `%do%`
}
foreach(i=seq_len(n), .combine='c') %d% {
Sys.sleep(1)
i
}
}
Это написано так, что он работает только параллельно, если parallel=TRUE
, даже если они зарегистрировали параллельный бэкенд:
> parfun()
Executing parfun sequentially
[1] 1 2 3 4 5 6 7 8 9 10
Если parallel=TRUE
и они не имеют зарегистрировал бэкэнд, затем он создаст и зарегистрирует объект кластера для них:
> parfun(parallel=TRUE, cores=3)
Registered doParallel with 3 workers
[1] 1 2 3 4 5 6 7 8 9 10
Если parfun
вызывается с parallel=TRUE
снова, он будет использовать ранее зарегистрированную кластер:
> parfun(parallel=TRUE)
Using doParallelSNOW with 3 workers
[1] 1 2 3 4 5 6 7 8 9 10
Это может быть уточнена во многих отношениях: это всего лишь простая демонстрация. Но, по крайней мере, это обеспечивает удобство, не мешая пользователям регистрировать другой сервер с настраиваемыми параметрами, которые могут потребоваться в их среде.
Обратите внимание, что выбор номера по умолчанию ядер/рабочие также сложный вопрос, и один, что CRAN Сопровождающие небезразличен. Вот почему я не сделал число ядер по умолчанию detectCores()
. Вместо этого я использую метод, используемый mclapply
, хотя, возможно, следует использовать другое имя опции.
Относительно stopCluster
Обратите внимание, что этот пример будет иногда создать новый объект кластера, но он никогда не останавливает его через вызов stopCluster
. Причина в том, что создание объектов кластера может быть дорогостоящим, поэтому я хотел бы повторно использовать их для нескольких циклов foreach, а не создавать и уничтожать их каждый раз. Однако я бы предпочел оставить это для пользователя, однако в этом примере пользователю не удастся это сделать, поскольку они не имеют доступа к переменной cl
.
Есть три способа справиться с этим:
- Вызов
stopCluster
в parfun
когда makePSOCKcluster
называется;
- Напишите дополнительную функцию, которая позволяет пользователю остановить неявно созданный объект кластера (что эквивалентно функции
stopImplicitCluster
в пакете doParallel
);
- Не беспокойтесь о неявно созданном объекте кластера.
Я бы выбрал второй вариант для своего собственного кода, но это значительно усложнило бы этот пример. Это уже довольно сложно.
Это похоже на правильный баланс поддержки для простой, встроенной параллелизации и поддержки для более продвинутых пользователей. Большое спасибо за ваше время. – C8H10N4O2
Как бы вы остановили Cluster (cl), то? Оставить его пользователю? – JPMac
@JPMac Как насчет 'on.exit (stopCluster (cl))' после 'cl <- makePSOCKcluster (ядра)' – Marek