Вложенность параллельных функций в 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 для запуска одного работника на ядро на каждом из этих узлов.
Обратите внимание, что ваш пример не использует много вычислительного времени, поэтому не стоит использовать два уровня параллелизма. Я бы использовал гораздо большую проблему, чтобы определить преимущества различных подходов.