我有一个通用的分块功能,可以将大调用分成小块并并行运行。
chunk_it <- function(d, n, some_fun) {
# run n chunks of d in parallel
dat <- foreach(...) %doPar% {
some_fun(...)
}
}
我想这样做,以便这个通用的分块功能可以识别它是否已被并行的进程调用(在我的术语中分块)
chunked_highlevel <- function(d, n, some_fun) {
# run n chunks of d in parallel
...
chunk_it(lowerlevel_d, n) # do not chunk!
}
我想在这里发生的是,如果我将进程分级到更高级别,那么它不会激活较低级别的分块功能。
有没有办法确定您何时已进入并行流程?
所以,我们可以像这样编码:
chunk_it <- function(d, n, some_fun) {
# run n chunks of d in parallel
if(!already_parallel) {
dat <- foreach(...) %doPar% {
some_fun(...)
}
} else {
dat <- some_fun()
}
}
答案 0 :(得分:1)
我认为这不是正式的做法。但是,一般来说,调用堆栈中应该有明显的代码,这使得你是否可以使用并行代码。到目前为止我得到的是这样的。它似乎适用于doSNOW
MPI或SOCK,但可能需要调整其他实现%dopar%
的包。它还依赖于snow
的某些内部细节,这些细节可能会在将来的版本中发生变化。
library(doSNOW)
library(foreach)
my_fn <- function(bit) {
is_parallel <- any(unlist(lapply(sys.calls(), function(cal) {
as.character(cal[[1]]) %in% c("slaveLoop", "%dopar%")
})))
is_parallel
}
foreach(x = 1:2) %do% my_fn(x)
# [[1]]
# [1] FALSE
#
# [[2]]
# [1] FALSE
cl <- makeCluster(2)
registerDoSNOW()
foreach(x = 1:2) %dopar% my_fn(x)
# [[1]]
# [1] TRUE
#
# [[2]]
# [1] TRUE
答案 1 :(得分:1)
future包(我是作者)已经内置了对嵌套并行性的支持,因此您不必担心它作为开发人员,同时仍然让最终用户有能力控制如何以及在何处正在进行并行化。
以下是其中一个future vignettes:
的示例library("future")
library("listenv")
x <- listenv()
for (ii in 1:3) {
x[[ii]] %<-% {
y <- listenv()
for (jj in 1:3) {
y[[jj]] %<-% { ii + jj/10 }
}
y
}
}
unlist(x)
## [1] 1.1 1.2 1.3 2.1 2.2 2.3 3.1 3.2 3.3
请注意未来分配有两层(%<-%
)。 默认设置是始终按顺序处理,除非具体说明。例如,要在本地计算机上并行处理未来分配的外部循环,请使用:
plan(multiprocess)
这会导致x[[ii]] %<-% { ... }
ii = 1, 2, 3
并行运行,而包含的y[[jj]] %<-% { ... }
将按顺序运行。对此的等效完全显式设置是:
plan(list(multiprocess, sequential))
现在,如果你想顺序运行期货的外部循环(x[[ii]]
)和期货的内部循环(y[[jj]]
),你可以指定:
plan(list(sequential, multiprocess))
在运行代码之前。
BTW,与multiprocess
一起使用的并行进程数为future::availableCores()
。可以将其视为parallel::detectCores()
但对mc.cores
,HPC群集环境等也是敏捷的。重要的是,future::availableCores()
如果已经并行运行,则会返回1
(“是平行的孩子“)。这意味着,如果你这样做:
plan(list(multiprocess, multiprocess))
期货的内层实际上只会看到一个核心。您可以将此视为内置的自动保护,防止通过递归并行性错误地创建大量并行进程。
您可以强制使用其他设置(但不推荐)。例如,假设您希望外层同时运行四个并行任务,并且每个任务同时运行两个并行任务(在本地计算机上),那么您可以使用:
plan(list(
tweak(multiprocess, workers = 4L),
tweak(multiprocess, workers = 2L)
))
这将同时运行最多4 * 2 = 8个并行任务(加上主进程)。
如果您有一组可用的机器,您可以这样做:
plan(list(
tweak(cliuster, workers = c("machine1", "machine2", "machine3")),
multiprocess
))
将期货的外层(x[[ii]]
)分配给这三台机器,而期货的内层(y[[ii]]
)将使用这些机器上的所有可用内核并行运行。 / p>
注意代码不会改变 - 只有设置(= plan()
调用)。这是“一次编写,随处运行”的精神。您可以使用许多不同的未来策略设置;看看未来包装的小插曲。
现在,如果你想使用foreach()
怎么办?您可以使用在未来框架之上工作的doFuture %dopar%
适配器。例如,
library("doFuture")
registerDoFuture()
some_fun <- function(j) {
list(j = j, pid.j = Sys.getpid())
}
my_fun <- function(i) {
y <- foreach(j = 1:3) %dopar% { some_fun(j = j) }
list(i = i, pid.i = Sys.getpid(), y = y)
}
x <- foreach(i = 1:3) %dopar% { my_fun(i = i) }
运行上面的内容并查看str(x)
及其不同的PID,以了解上面列举的不同plan()
:s。这将说明正在发生的事情。
希望这有帮助