5

我熟悉foreach%dopar%之类的。我也parallel熟悉cv.glmnet. 但是您如何设置嵌套并行,如下所示?

library(glmnet)
library(foreach)
library(parallel)
library(doSNOW)
Npar <- 1000
Nobs <- 200
Xdat <- matrix(rnorm(Nobs * Npar), ncol = Npar)
Xclass <- rep(1:2, each = Nobs/2)
Ydat <- rnorm(Nobs)

并行交叉验证:

cl <- makeCluster(8, type = "SOCK")
registerDoSNOW(cl)
system.time(mods <- foreach(x = 1:2, .packages = "glmnet") %dopar% {
    idx <- Xclass == x
    cv.glmnet(Xdat[idx,], Ydat[idx], nfolds = 4, parallel = TRUE)
})
stopCluster(cl)

不是并行交叉验证:

cl <- makeCluster(8, type = "SOCK")
registerDoSNOW(cl)
system.time(mods <- foreach(x = 1:2, .packages = "glmnet") %dopar% {
    idx <- Xclass == x
    cv.glmnet(Xdat[idx,], Ydat[idx], nfolds = 4, parallel = FALSE)
})
stopCluster(cl)

对于这两个系统时间,我只得到了非常微小的差异。

是并行化的吗?还是我需要明确使用嵌套运算符?

附带问题:如果集群对象中有 8 个内核可用并且foreach循环包含两个任务,每个任务会被分配 1 个内核(其他 6 个内核空闲)还是每个任务会被分配 4 个内核(用完所有 8 个内核总共)?查询在给定时间正在使用多少个内核的方法是什么?

4

1 回答 1

3

在您的并行交叉验证示例中, cv.glmnet 本身不会并行运行,因为集群工作人员中没有注册 foreach 并行后端。外部 foreach 循环将并行运行,但不是 cv.glmnet 函数中的 foreach 循环。

要将 doSNOW 用于外部和内部 foreach 循环,您可以使用 clusterCall 初始化雪集群工作人员:

cl <- makeCluster(2, type = "SOCK")
clusterCall(cl, function() {
  library(doSNOW)
  registerDoSNOW(makeCluster(2, type = "SOCK"))
  NULL
})
registerDoSNOW(cl)

这会为 master 和 worker 注册 doSNOW,这样每次调用 cv.glmnet 都会在parallel=TRUE指定时在两个 worker 集群上执行。

嵌套并行的技巧是避免创建过多的进程和过度订阅 CPU(或 CPU),因此在注册并行后端时需要小心。我的示例对于具有四个内核的 CPU 是有意义的,即使总共创建了六个工作程序,因为“外部”工作程序在内部 foreach 循环执行时不会做太多事情。在集群上运行时,通常使用 doSNOW 为每个节点启动一个工作程序,然后使用 doMC 在每个节点上的每个核心上启动一个工作程序。

请注意,您的示例不使用太多计算时间,因此使用两级并行性并不值得。我会使用一个更大的问题来确定不同方法的好处。

于 2014-01-23T22:52:21.903 回答