确定是否并行呼叫

时间:2017-03-18 21:39:16

标签: r parallel-processing

我有一个通用的分块功能,可以将大调用分成小块并并行运行。

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()
  }
}

2 个答案:

答案 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。这将说明正在发生的事情。

希望这有帮助