Есть ли эффективный способ распараллелить mapply?

У меня много строк, и в каждой строке я вычисляю одночлен нелинейной функции. У меня четырехъядерная машина Ubuntu, которая не перестает выполнять мой код уже два дня. Неудивительно, что я ищу способы ускорить работу ;-)

После некоторого исследования я заметил, что в настоящее время используется только одно ядро, а распараллеливание - это то, что нужно. Копая глубже, я пришел к выводу (возможно, ошибочному?), что пакет foreach не очень подходит для моей задачи, потому что получается слишком много накладных расходов (см., например, SO). Хорошей альтернативой представляется multicore для Unix-машин. В частности, функция pvec кажется наиболее эффективной после того, как я проверил страницу помощи.

Однако, если я правильно понимаю, эта функция принимает только один вектор и разбивает его соответствующим образом. Мне нужна функция, которая может быть распараллелена, но принимает несколько векторов (или data.frame вместо этого), как это делает функция mapply. Может быть, я что-то упустил?

Вот небольшой пример того, что я хочу сделать: (Обратите внимание, что я включил сюда пример plyr, потому что он может быть альтернативой базовой функции mapply и имеет опцию параллелизации. Однако в моей реализации она работает медленнее, а внутри она вызывает foreach для распараллеливания, поэтому я думаю, что это не поможет. Правильно ли это?)

library(plyr)
library(foreach)
n <- 10000
df <- data.frame(P   = rnorm(n, mean=100, sd=10),
                 B0  = rnorm(n, mean=40,  sd=5),
                 CF1 = rnorm(n, mean=30,  sd=10),
                 CF2 = rnorm(n, mean=30,  sd=5),
                 CF3 = rnorm(n, mean=90,  sd=8))

get_uniroot <- function(P, B0, CF1, CF2, CF3) {

  uniroot(function(x) {-P + B0 + CF1/x + CF2/x^2 + CF3/x^3}, 
          lower = 1,
          upper = 10,
          tol   = 0.00001)$root

}

system.time(x1 <- mapply(get_uniroot, df$P, df$B0, df$CF1, df$CF2, df$CF3))
   #user  system elapsed 
   #0.91    0.00    0.90 
system.time(x2 <- mdply(df, get_uniroot))
   #user  system elapsed 
   #5.85    0.00    5.85
system.time(x3 <- foreach(P=df$P, B0=df$B0, CF1=df$CF1, CF2=df$CF2, CF3=df$CF3, .combine = "c") %do% {
    get_uniroot(P, B0, CF1, CF2, CF3)})
   #user  system elapsed 
  # 10.30    0.00   10.36
all.equal(x1, x2$V1) #TRUE
all.equal(x1, x3)    #TRUE

Также я попытался реализовать функцию Райана Томпсона chunkapply из ссылки SO выше (только избавился от части doMC, потому что не смог ее установить. Однако его пример работает, даже после корректировки его функции), но не смог заставить ее работать. Однако, поскольку он использует foreach, я подумал, что к нему применимы те же аргументы, о которых говорилось выше, поэтому не стал долго пытаться.

#chunkapply(get_uniroot, list(P=df$P, B0=df$B0, CF1=df$CF1, CF2=df$CF2, CF3=df$CF3))
#Error in { : task 1 failed - "invalid function value in 'zeroin'"

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

6
задан Community 23 May 2017 в 12:09
поделиться