Я пытаюсь сравнить варианты распараллеливания.В частности, я сравниваю стандартные реализации SNOW
и mulitcore
с теми, которые используют doSNOW
или doMC
и foreach
.В качестве примера задачи я иллюстрирую центральную предельную теорему, многократно вычисляя средние значения выборок из стандартного нормального распределения.Вот стандартный код:
CltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
sapply(1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
})
}
Вот реализация SNOW
:
library(snow)
cl <- makeCluster(2)
ParCltSim <- function(cluster, nSims=1000, size=100, mu=0, sigma=1){
parSapply(cluster, 1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
})
}
Далее, метод doSNOW
:
library(foreach)
library(doSNOW)
registerDoSNOW(cl)
FECltSim <- function(nSims=1000, size=100, mu=0, sigma=1) {
x <- numeric(nSims)
foreach(i=1:nSims, .combine=cbind) %dopar% {
x[i] <- mean(rnorm(n=size, mean=mu, sd=sigma))
}
}
Я получаюследующие результаты:
> system.time(CltSim(nSims=10000, size=100))
user system elapsed
0.476 0.008 0.484
> system.time(ParCltSim(cluster=cl, nSims=10000, size=100))
user system elapsed
0.028 0.004 0.375
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
8.865 0.408 11.309
Реализация SNOW
экономит около 23% вычислительного времени по сравнению с непараллельным прогоном (экономия времени увеличивается по мере увеличения числа симуляций, как и следовало ожидать).Попытка foreach
фактически увеличивает время выполнения в 20 раз. Кроме того, если я изменяю %dopar%
на %do%
и проверяю непараллельную версию цикла, это занимает более 7 секунд.
Кроме того, мы можем рассмотреть пакет multicore
.Симуляция, написанная для multicore
:
library(multicore)
MCCltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
unlist(mclapply(1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
}))
}
Мы получаем улучшение скорости еще лучше, чем SNOW
:
> system.time(MCCltSim(nSims=10000, size=100))
user system elapsed
0.924 0.032 0.307
Начиная новую сессию R, мы можем попытаться foreach
реализация, использующая doMC
вместо doSNOW
, вызывающая
library(doMC)
registerDoMC()
, затем выполняющая FECltSim()
, как указано выше, все еще находящая
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
6.800 0.024 6.887
Это "только" в 14 разувеличение по сравнению с непараллельным временем выполнения.
Вывод: Мой код foreach
не работает эффективно ни при doSNOW
, ни doMC
.Есть идеи, почему?
Спасибо, Чарли