Почему параллельный пакет работает медленнее, чем просто использовать?

Я пытаюсь определить, когда использовать parallel пакет, чтобы ускорить время, необходимое для выполнения некоторого анализа. Одна из вещей, которую мне нужно сделать, это создать матрицы, сравнивающие переменные в двух кадрах данных с различным количеством строк. Я задал вопрос об эффективном способе работы с StackOverflow и написал о тестах в своем блоге . Поскольку мне комфортно с наилучшим подходом, я хотел ускорить процесс, выполнив его параллельно. Ниже приведенные результаты основаны на 2gz i7 Mac с 8 ГБ оперативной памяти. Я удивлен, что parallel пакет, в частности, parSapply , хуже, чем просто использовать функцию apply . Код для его повторения ниже. Обратите внимание, что в настоящее время я использую только один из двух создаваемых мной столбцов, но в конечном итоге хочу использовать оба.

Время выполнения http://sofru.miximages.com/r/ParalleVsApplyTiming.png

 require(parallel) require(ggplot2) require(reshape2) set.seed(2112) results <- list() sizes <- seq(1000, 30000, by=5000) pb <- txtProgressBar(min=0, max=length(sizes), style=3) for(cnt in 1:length(sizes)) { i <- sizes[cnt] df1 <- data.frame(row.names=1:i, var1=sample(c(TRUE,FALSE), i, replace=TRUE), var2=sample(1:10, i, replace=TRUE) ) df2 <- data.frame(row.names=(i + 1):(i + i), var1=sample(c(TRUE,FALSE), i, replace=TRUE), var2=sample(1:10, i, replace=TRUE)) tm1 <- system.time({ df6 <- sapply(df2$var1, FUN=function(x) { x == df1$var1 }) dimnames(df6) <- list(row.names(df1), row.names(df2)) }) rm(df6) tm2 <- system.time({ cl <- makeCluster(getOption('cl.cores', detectCores())) tm3 <- system.time({ df7 <- parSapply(cl, df1$var1, FUN=function(x, df2) { x == df2$var1 }, df2=df2) dimnames(df7) <- list(row.names(df1), row.names(df2)) }) stopCluster(cl) }) rm(df7) results[[cnt]] <- c(apply=tm1, parallel.total=tm2, parallel.exec=tm3) setTxtProgressBar(pb, cnt) } toplot <- as.data.frame(results)[,c('apply.user.self','parallel.total.user.self', 'parallel.exec.user.self')] toplot$size <- sizes toplot <- melt(toplot, id='size') ggplot(toplot, aes(x=size, y=value, colour=variable)) + geom_line() + xlab('Vector Size') + ylab('Time (seconds)') 

Запуск рабочих мест параллельно приводит к накладным расходам. Только если рабочие места, которые вы запускаете на рабочих узлах, занимаете значительное количество времени, распараллеливание повышает общую производительность. Когда отдельные задания занимают всего миллисекунды, накладные расходы на постоянное отключение рабочих мест ухудшат общую производительность. Трюк состоит в том, чтобы разделить работу над узлами таким образом, чтобы задания были достаточно длинными, скажем, по крайней мере несколько секунд. Я использовал это с большим успехом, работая одновременно с шестью моделями Fortran, но эти отдельные модели выполняли часы, почти отрицая эффект накладных расходов.

Обратите внимание, что я не запускал ваш пример, но ситуация, о которой я рассказываю выше, часто является проблемой, когда парализация занимает больше времени, чем выполняется последовательно.

Эти различия могут быть связаны с 1) накладными расходами на связь (особенно, если вы работаете через узлы) и 2) служебными служебными данными (если ваша работа не такая интенсивная по сравнению с инициированием параллелизации, например). Обычно, если задача, которую вы распараллеливаете, не требует много времени, тогда вы в основном обнаружите, что параллелизация не имеет большого эффекта (что очень хорошо видно на огромных наборах данных.

Даже если это не может напрямую ответить на ваш бенчмаркинг, я надеюсь, что это должно быть довольно простым и может быть связано. В качестве примера здесь я data.frame с 1e6 строками с 1e4 уникальными 1e4 столбца столбца и некоторыми значениями в столбце val . И затем я запускаю plyr parallel с помощью doMC и без параллелизации.

 df <- data.frame(group = as.factor(sample(1:1e4, 1e6, replace = T)), val = sample(1:10, 1e6, replace = T)) > head(df) group val # 1 8498 8 # 2 5253 6 # 3 1495 1 # 4 7362 9 # 5 2344 6 # 6 5602 9 > dim(df) # [1] 1000000 2 require(plyr) require(doMC) registerDoMC(20) # 20 processors # parallelisation using doMC + plyr P.PLYR <- function() { o1 <- ddply(df, .(group), function(x) sum(x$val), .parallel = TRUE) } # no parallelisation PLYR <- function() { o2 <- ddply(df, .(group), function(x) sum(x$val), .parallel = FALSE) } require(rbenchmark) benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed") test replications elapsed relative user.self sys.self user.child sys.child 2 PLYR() 2 8.925 1.000 8.865 0.068 0.000 0.000 1 P.PLYR() 2 30.637 3.433 15.841 13.945 8.944 38.858 

Как вы можете видеть, параллельная версия plyr работает в 3,5 раза медленнее

Теперь позвольте мне использовать тот же data.frame , но вместо вычисления sum позвольте мне построить немного более требовательную функцию, скажем, median(.) * median(rnorm(1e4) ((бессмысленно, да):

Вы увидите, что приливы начинают смещаться:

 # parallelisation using doMC + plyr P.PLYR <- function() { o1 <- ddply(df, .(group), function(x) median(x$val) * median(rnorm(1e4)), .parallel = TRUE) } # no parallelisation PLYR <- function() { o2 <- ddply(df, .(group), function(x) median(x$val) * median(rnorm(1e4)), .parallel = FALSE) } > benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed") test replications elapsed relative user.self sys.self user.child sys.child 1 P.PLYR() 2 41.911 1.000 15.265 15.369 141.585 34.254 2 PLYR() 2 73.417 1.752 73.372 0.052 0.000 0.000 

Здесь параллельная версия в 1.752 times быстрее, чем непараллельная версия.

Редактировать: после комментария @ Paul, я просто внедрил небольшую задержку с помощью Sys.sleep() . Конечно, результаты очевидны. Но только ради полноты, вот результат на 20 * 2. data.frame:

 df <- data.frame(group=sample(letters[1:5], 20, replace=T), val=sample(20)) # parallelisation using doMC + plyr P.PLYR <- function() { o1 <- ddply(df, .(group), function(x) { Sys.sleep(2) median(x$val) }, .parallel = TRUE) } # no parallelisation PLYR <- function() { o2 <- ddply(df, .(group), function(x) { Sys.sleep(2) median(x$val) }, .parallel = FALSE) } > benchmark(P.PLYR(), PLYR(), replications = 2, order = "elapsed") # test replications elapsed relative user.self sys.self user.child sys.child # 1 P.PLYR() 2 4.116 1.000 0.056 0.056 0.024 0.04 # 2 PLYR() 2 20.050 4.871 0.028 0.000 0.000 0.00 

Разница здесь не удивительна.

Полностью согласен с аргументами @Arun и @PaulHiemestra о том, почему …? часть вашего вопроса.

Однако кажется, что вы можете воспользоваться некоторыми преимуществами parallel пакета в своей ситуации (по крайней мере, если вы не застряли в Windows). Возможным решением является использование mclapply вместо parSapply , которое основывается на быстром форсировании и общей памяти.

  tm2 <- system.time({ tm3 <- system.time({ df7 <- matrix(unlist(mclapply(df2$var1, FUN=function(x) {x==df1$var1}, mc.cores=8)), nrow=i) dimnames(df7) <- list(row.names(df1), row.names(df2)) }) }) 

Конечно, nested system.time здесь не требуется. С моими 2 ядрами я получил:

введите описание изображения здесь

  • Импорт файлов Excel в R, xlsx или xls
  • Как я могу извлечь диапазоны осей для объекта ggplot2?
  • Как разбить фрейм данных?
  • Прогнозирование временных рядов данных
  • Как можно объединить две строки?
  • Использование ~ (тильда) в R-программировании Язык
  • ggplot2 - добавление вторичной оси y поверх графика
  • Как объединить несколько условий для подмножества кадра данных с помощью «ИЛИ»?
  • Функция MATCH в r
  • Сделать частотную гистограмму для переменных факторов
  • Способ всегда уклоняться от гистограммы?
  • Давайте будем гением компьютера.