Могу ли я использовать параллельную версию для l oop и применить семейство вместе? - PullRequest
0 голосов
/ 28 февраля 2020

Недавно я столкнулся с одной проблемой, когда проводил исследование: сначала я определил функцию myfunction, которая содержит два цикла for, а затем я использую lapply(datalist, myfunction), но обработка идет слишком медленно.

Затем я выучил два параллельных пакета 'foreach' и 'parallel' для параллельных вычислений. Поэтому я изменил оба процесса на их параллельные версии.

НО я обнаружил, что когда я запускаю свой код, кажется, что foreach в моей функции не работает.

myfunction <- function{data} {

   df  <- foreach (i = 1:200, .combine = "rbind") %:% 
    foreach(j = 1:200, .combine = "rbind") %dopar% {

      *****
      process
      *****
    }

  data <- df[1,1]
  return(data)
}

system.time({

  cl <- detectCores()
  cl <- makeCluster(cl)
  registerDoParallel(cl)

  mat <- t(parSapply(cl, list, myfuntion))

  stopCluster(cl)

}) 

Мне кажется, это из-за того, что parSapply занял все ядра, поэтому foreach не имеет дополнительных ядер для вычислений. Есть ли хорошая идея, чтобы это исправить? По сути, я хочу, чтобы оба процесса работали в параллельных версиях.

Другая проблема: предположим, что мы можем выбрать только один процесс для параллельных вычислений, какой мне выбрать? Для l oop или семейного применения?

Очень ценится:)

1 Ответ

0 голосов
/ 29 февраля 2020

Мне кажется, что это связано с тем, что parSapply занял целые ядра, поэтому у foreach нет дополнительных ядер для вычислений. Есть ли хорошая идея, чтобы это исправить? По сути, я хочу, чтобы оба процесса работали в параллельных версиях.

Нет, это не очень хорошая идея. В основном вы пытаетесь перепараллелизовать здесь (но это действительно происходит в вашем коде, как описано ниже).

Другая проблема: предположим, что мы можем выбрать только один процесс для параллельных вычислений, какой из них выбрать? Для семьи oop или подать заявку?

На этот вопрос нет единственно правильного ответа. Я рекомендую вам профилировать свой код *** process ***, чтобы выяснить, насколько он выигрывает от распараллеливания.

Итак, я нашел ваш parSapply(cl, ...) сверху foreach() %dopar% { ... } с использованием того же кластера cl интересным. Первый раз, когда я увидел это, спросил / предложил таким образом. Вы не хотите делать это наверняка, но вопрос / попытка не сумасшедшая. Ваша интуиция о том, что все работники будут заняты, когда foreach() %dopar% { ... } попытается использовать их, отчасти верна. Однако на самом деле происходит также то, что оператор foreach() %dopar% { ... } оценивается в working , а не в основном сеансе R, где был определен кластер cl. На рабочих адаптеры foreach не зарегистрированы, поэтому по умолчанию эти вызовы будут обрабатываться последовательно (== foreach::registerDoSEQ()). Чтобы добиться вложенного распараллеливания, вам нужно было настроить и зарегистрировать кластер внутри каждого работника, например, внутри функции myfunction().

Как автор future framework, я ' Я хотел бы предложить вам использовать это. Это защитит вас от вышеуказанных ошибок и не будет слишком параллельным (вы можете сделать это, если вы действительно хотите это сделать). Вот как я бы переписал ваш пример кода:

library(foreach) ## foreach() and %dopar%

myfunction <- function{data} {
   df  <- foreach(i = 1:200, .combine = "rbind") %:% 
    foreach(j = 1:200, .combine = "rbind") %dopar% {
      *****
      process
      *****
    }

  data <- df[1,1]
  return(data)
}


## Tell foreach to parallelize via the future framework
doFuture::registerDoFuture()

## Have the future framework parallelize using a cluster of
## local workers (similar to makeCluster(detectCores()))
library(future)
plan(multisession)

library(future.apply) ## future_sapply()

system.time({
  mat <- t(future_sapply(list, myfuntion))
})

Теперь важно понять, что внешнее распараллеливание future_sapply() будет работать в кластере с несколькими сеансами. Когда вы добираетесь до внутреннего распараллеливания foreach() %dopar% { ... }, все, что видит foreach, является последовательным рабочим, так что внутренний уровень будет обрабатываться параллельно. Это то, что я имею в виду, что будущая инфраструктура автоматически защитит вас от чрезмерного распараллеливания.

Если вы хотите, чтобы внутренний слой распараллеливался на кластере с несколькими сеансами, а внешний - последовательным, вы можете установите это как:

plan(list(sequential, multisession))

Если вы действительно хотите выполнить вложенное распараллеливание, скажем, двух работников внешнего уровня и четырех работников внутреннего уровня, вы можете использовать:

plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))

Это будет запускать 2 * 4 = 8 параллельных процессов R одновременно.

Что более полезно, если у вас есть несколько доступных компьютеров, то вы можете использовать их для внешнего уровня, а затем использовать многосессионный кластер на каждый из них. Что-то вроде:

plan(list(tweak(cluster, workers = c("machine1", "machine2")), multisession))

Подробнее об этом можно прочитать в будущих виньетках.

...