Оптимизация процесса кода в допар - PullRequest
0 голосов
/ 03 марта 2020

Я пытаюсь оптимизировать мой код для запуска glms несколько раз, и я хотел бы использовать распараллеливание, либо с foreach, либо каким-либо другим более эффективным способом.

Как видите; for l oop занимает около 800 секунд, чтобы запустить 270000 glms; в то время как foreach с dopar не всегда интуитивно воспринимается навсегда (либо происходит сбой, либо я заставляю его остановиться через пару часов).

Спасибо за вашу помощь.

Jine sh

library(data.table)
library(parallel)
library(doParallel)
library(foreach)
scen_bin <- expand.grid(n = c(10, 20, 30), rate1 = c(0.1, 0.2, 0.3),
  rate2 = c(0.5, 0.6, 0.9))

rep <- 10000
scen_sims <- rbindlist(replicate(rep, scen_bin, simplify = FALSE),
  idcol = TRUE)
scen_sims[, `:=`(glm, list(c(1L, 2L)))]

for (i in 1:270000) {
  set(scen_sims, i, 8L, list(glm(formula = c(rbinom(scen_sims$drug[i], 1L, scen_sims$Treatment_Rates[i]),
    rbinom(scen_sims$control[i], 1L, scen_sims$Comparator_Rates[i])) ~ factor(c(rep("Trt",
    scen_sims$drug[i]), rep("Cont", scen_sims$control[i]))), family = "binomial")))
}

split_scen_sims <- split(scen_sims, seq(1, 270000, length.out = 1000))


jh <- foreach(x = 1:1000, .packages = c("data.table")) %dopar% {
  jh <- split_scen_sims[[x]]
  for (i in 1:270000) {
    set(jh, i, 8L, list(glm(formula = c(rbinom(jh$n[i], 1L, jh$rate1[i]), rbinom(jh$n[i],
      1L, jh$rate1[i])) ~ factor(c(rep("Trt", jh$n[i]), rep("Cont", jh$n[i]))),
      family = "binomial")))
  }
  return(jh)
}

1 Ответ

1 голос
/ 03 марта 2020

Первое, что следует отметить, это то, что использование функции извлечения $ в пределах al oop делает это плохо. Было бы лучше: 1) сделать функцию, а затем 2) использовать обычный вызов data.table.

fx_make_glm = function(drug, treat_rate, control, Comparator_Rates){
  glm(formula = c(rbinom(drug, 1L, treat_rate),
                  rbinom(control, 1L, Comparator_Rates)) ~
        factor(c(rep("Trt", drug), rep("Cont", control))), 
      family = "binomial")
}

Это значительно упростит все остальное - я буду использовать Map, который будет l oop через каждый элемент переменных, представляющих интерес:

scen_sims[, glm := list(Map(fx_make_glm, n, rate1, n, rate2))]

К сожалению, это все еще не обеспечивает такую ​​производительность, как идеальная: (

Unit: seconds
     expr  min   lq mean median   uq  max neval
  OP_loop 3.01 3.21 3.21   3.22 3.26 3.36     5
 map_call 2.64 2.89 2.90   2.92 2.96 3.08     5

Мой параллельный пакет выбора future.apply - просто поставьте future_ перед серией *apply, и вы получите параллельную оценку:

library(future.apply)
plan(multiprocess)
system.time({
  scen_sims[, glm := list(future_Map(fx_make_glm, n, rate1, n, rate2))]
})

   user  system elapsed 
   1.22    0.13    3.22 

## truncated the microbenchmark call

Unit: seconds
            expr  min   lq mean median   uq  max neval
         OP_loop 2.93 2.98 3.08   3.00 3.18 3.32     5
        map_call 2.65 2.70 2.94   2.89 3.18 3.25     5
 future_map_call 2.84 3.24 3.37   3.43 3.49 3.85     5

Я на Windows с 2 ядрами / 4 потоками. Если бы я был на Linux, Я бы попробовал plan(multicore), чтобы увидеть, были ли процессы разветвления более производительными.

Генерация данных:

library(data.table)
## generate data
scen_bin <- expand.grid(n = c(10, 20, 30), rate1 = c(0.1, 0.2, 0.3),
                        rate2 = c(0.5, 0.6, 0.9))

rep <- 50L
scen_sims <- rbindlist(replicate(rep, scen_bin, simplify = FALSE),
                       idcol = TRUE)
scen_sims[, `:=`(glm, list(c(1L, 2L)))]
...