Вложенность параллельных функций в R (

Я знаком с 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 ядер останутся бездействующими) или каждая задача получит четыре ядра (всего будет задействовано все 8 ядер)? Как узнать, сколько ядер используется в данный момент?

1 ответ

Решение

В вашем примере параллельной перекрестной проверки cv.glmnet не будет работать параллельно, потому что в работниках кластера не зарегистрирован ни один параллельный бэкэнд foreach. Внешний цикл foreach будет работать параллельно, но не цикл foreach в функции cv.glmnet.

Чтобы использовать doSNOW для внешнего и внутреннего циклов foreach, вы можете инициализировать работников снежного кластера с помощью clusterCall:

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

Это регистрирует doSNOW и для главного, и для рабочих, так что каждый вызов cv.glmnet будет выполняться в кластере с двумя рабочими, когда parallel=TRUE указан.

Хитрость с вложенным параллелизмом состоит в том, чтобы избежать создания слишком большого количества процессов и переподписки ЦП (или ЦП), поэтому вам нужно быть осторожным при регистрации параллельных бэкэндов. Мой пример имеет смысл для ЦП с четырьмя ядрами, хотя в общей сложности создано шесть рабочих, так как "внешние" рабочие не делают много, пока выполняются внутренние циклы foreach. При работе в кластере обычно используют doSNOW для запуска одного работника на узел, а затем используют doMC для запуска одного работника на ядро ​​на каждом из этих узлов.

Обратите внимание, что ваш пример не использует много вычислительного времени, поэтому не стоит использовать два уровня параллелизма. Я бы использовал гораздо большую проблему, чтобы определить преимущества различных подходов.

Другие вопросы по тегам