Почему фьючерсы второго уровня исполняются последовательно? - PullRequest
0 голосов
/ 11 июня 2018

Я пытаюсь воспроизвести примеры в виньетке топологии пакета future .Процитируем:

Фьючерсы могут быть вложены в R так, что одно будущее создает другой набор фьючерсов и так далее.Это может, например, происходить в рамках вложенных циклов for [...]

В части, где автор использует plan(list(multicore, multicore)) (дополнительные аргументы и tweak опущены) для синхронной обработки двух фьючерсовкто в семестре обрабатывает четыре фьючерса синхронно.Это должно равняться восьми фьючерсам, обрабатываемым синхронно.

Однако, когда я пытаюсь воспроизвести это с помощью приведенного ниже кода, я вижу, что второй уровень фьючерсов обрабатывается последовательно.Что я делаю не так?

MCVE

library(future)
library(ggplot2)
plan(list(multiprocess, multiprocess))


# Run for a random amount of time and return start and stop time
startStop <- function(){
  start <- Sys.time()
  x <- runif(1, 1, 3)
  Sys.sleep(x)
  stop <- Sys.time()
  return(data.frame(start = start, stop = stop))
}

nGrp <- 3
nCV <- 4

l <- rep(list(NULL), nGrp)


for(i in seq_along(l)){
  l[[i]] <- future({
    m <- rep(list(NULL), nCV)
    for(j in seq_along(m)){
      m[[j]] <- future(startStop())
    }
    m <- lapply(m, value)
    m <- do.call(rbind, m)
    m
  })
}
l <- lapply(l, value)
d <- do.call(rbind, l)
d$iGrp <- rep(seq_len(nGrp), each = nCV)
d$iCV <- rep(seq_len(nCV), times = nGrp)

d$x <- paste(d$iGrp, d$iCV, sep = "_")
d$iGrp <- as.character(d$iGrp)
ggplot(d, aes(x = x, ymin = start, ymax = stop, color = iGrp)) + geom_linerange() + coord_flip()

time evolution of futures execution

Ожидание

expectation

Информация о сеансе

R version 3.4.3 (2017-11-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS: /opt/Bio/R/3.4.3/lib64/R/lib/libRblas.so
LAPACK: /opt/Bio/R/3.4.3/lib64/R/lib/libRlapack.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggplot2_2.2.1 future_1.8.1 

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.17     devtools_1.13.4  munsell_0.4.3    colorspace_1.3-2
 [5] R6_2.2.2         rlang_0.1.6      httr_1.3.1       plyr_1.8.4      
 [9] globals_0.11.0   tools_3.4.3      parallel_3.4.3   grid_3.4.3      
[13] gtable_0.2.0     git2r_0.21.0     withr_2.1.1      yaml_2.1.16     
[17] lazyeval_0.2.1   digest_0.6.15    tibble_1.4.2     codetools_0.2-15
[21] curl_3.1         memoise_1.1.0    compiler_3.4.3   pillar_1.1.0    
[25] scales_0.5.0     listenv_0.7.0 

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Если вы хотите добиться параллельной обработки, как и ожидалось, то future.callr - выбор.Просто используйте: library(future.callr) plan(list(callr, callr))

0 голосов
/ 13 июня 2018

Автор future здесь: это потому, что есть встроенная защита от вложенного параллелизма.Без него вы бы перегружали компьютер слишком большим количеством параллельных процессов, что не только перегревало бы его, но и замедляло бы общую производительность.

Я обновил виньетку «Топологии будущего» для следующего выпуска с помощьюследующий раздел:

Встроенная защита от рекурсивного параллелизма

Выше мы параллельно обрабатывали либо внешний, либо внутренний набор будущего.Что если мы хотим обрабатывать оба слоя параллельно?Соблазнительно использовать:

plan(list(multiprocess, multiprocess))

Хотя это не дает ошибки, мы обнаружим, что внутренний слой фьючерсов будет обрабатываться последовательно, как если бы мы использовали plan(list(multiprocess, sequential)).Такое поведение обусловлено встроенной защитой от вложенного параллелизма.Если бы оба слоя работали параллельно, каждый из которых использовал бы 8 ядер, имеющихся на машине, мы бы запустили 8 * 8 = 64 параллельных процесса - это наверняка перегрузило бы наш компьютер.Что происходит внутри, так это то, что для внешнего слоя availableCores() равно восьми (8), тогда как для внутреннего слоя оно равно одному (1).

Теперь мы можем представить, что обрабатываем внешний слой,скажем, два параллельных фьючерса, а затем внутренний слой с четырьмя параллельными фьючерсами.В этом случае мы будем работать максимум на восьми ядрах (= 2 * 4).Это может быть достигнуто путем принудительного фиксирования числа рабочих на каждом слое (не рекомендуется):

plan(list(tweak(multiprocess, workers = 2), tweak(multiprocess, workers = 4)))
...