R параллельных вычислений и зомби-процессов

Это в основном следующий этот более специализированный вопрос. Были некоторые сообщения о создании процессов зомби при параллельных вычислениях в R:

Существует несколько способов параллельных вычислений, и я сосредоточусь на трех способах, которые я использовал до сих пор на локальной машине. Я использовал doMC и doParallel с пакетом foreach на локальном компьютере с ядрами 4:

(a) Регистрация кластера вилок:

library(doParallel)
cl <- makeForkCluster(4)
# equivalently here: cl <- makeForkCluster(nnodes=getOption("mc.cores", 4L))
registerDoParallel(cl)
    out <- foreach(i=1:1000, .combine = "c") %dopar% {
        print(i)
    }
stopCluster(cl)

(b) Регистрация кластера PSOCK:

library(doParallel)
cl <- makePSOCKcluster(4)
registerDoParallel(cl)
    out <- foreach(i=1:1000, .combine = "c") %dopar% {
        print(i)
    }
stopCluster(cl)

(c) Используя doMC

library(doMC)
library(doParallel)
registerDoMC(4)
    out <- foreach(i=1:1000, .combine = "c") %dopar% {
        print(i)
    }

Несколько пользователей заметили, что при использовании метода doMC, который является всего лишь оберткой для функции mclapply, поэтому его ошибка не doMC (см. здесь: How убить работника doMC, когда это будет сделано?) - оставляет процессы зомби позади. В ответе на предыдущий вопрос (Как остановить R от ухода за зомби-процессами за) было высказано предположение, что использование кластера вилок может не покидать процессы зомби. В другом вопросе было предложено (Удалить процессы зомби с использованием параллельного пакета), что использование кластера PSOCK может не покидать процессы зомби. Однако кажется, что все три метода оставляют процесс зомби. Хотя процессы зомби как таковые обычно не являются проблемой, потому что они (как правило) не связывают ресурсы, они загромождают дерево процессов. Тем не менее я могу избавиться от них, закрыв и снова открыв R, но это не лучший вариант, когда я нахожусь в середине сеанса. Есть ли объяснение, почему это происходит (или даже: есть ли причина, почему это должно произойти)? И нужно ли что-то сделать, чтобы не осталось никаких процессов зомби?

Моя системная информация (R используется в простом сеансе repl с xterm и tmux):

library(devtools)
> session_info()
Session info-------------------------------------------------------------------
 setting  value                                             
 version  R Under development (unstable) (2014-08-16 r66404)
 system   x86_64, linux-gnu                                 
 ui       X11                                               
 language (EN)                                              
 collate  en_IE.UTF-8                                       
 tz       <NA>                                              

Packages-----------------------------------------------------------------------
 package    * version  source          
 codetools    0.2.8    CRAN (R 3.2.0)  
 devtools   * 1.5.0.99 Github (c429ae2)
 digest       0.6.4    CRAN (R 3.2.0)  
 doMC       * 1.3.3    CRAN (R 3.2.0)  
 evaluate     0.5.5    CRAN (R 3.2.0)  
 foreach    * 1.4.2    CRAN (R 3.2.0)  
 httr         0.4      CRAN (R 3.2.0)  
 iterators  * 1.0.7    CRAN (R 3.2.0)  
 memoise      0.2.1    CRAN (R 3.2.0)  
 RCurl        1.95.4.3 CRAN (R 3.2.0)  
 rstudioapi   0.1      CRAN (R 3.2.0)  
 stringr      0.6.2    CRAN (R 3.2.0)  
 whisker      0.3.2    CRAN (R 3.2.0)  

Небольшое редактирование: по крайней мере, для makeForkCluster() кажется, что иногда вилки, которые он порождает, правильно убиваются и получаются родителем, а иногда они не получают жатву и становятся зомби. Кажется, это происходит только тогда, когда кластер не закрывается достаточно быстро после того, как цикл прерван или закончен; по крайней мере, это произошло, когда это произошло в последние несколько раз.

Ответ 1

Вы можете избавиться от процессов зомби, используя пакет "inline". Просто реализуйте функцию, которая вызывает "waitpid":

library(inline)
includes <- '#include <sys/wait.h>'
code <- 'int wstat; while (waitpid(-1, &wstat, WNOHANG) > 0) {};'
wait <- cfunction(body=code, includes=includes, convention='.C')

Я проверил это, сначала создав некоторых зомби с функцией mclapply:

> library(parallel)
> pids <- unlist(mclapply(1:4, function(i) Sys.getpid(), mc.cores=4))
> system(paste0('ps --pid=', paste(pids, collapse=',')))
  PID TTY          TIME CMD
17447 pts/4    00:00:00 R <defunct>
17448 pts/4    00:00:00 R <defunct>
17449 pts/4    00:00:00 R <defunct>
17450 pts/4    00:00:00 R <defunct>

(Обратите внимание, что я использую версию GNU "ps", которая поддерживает параметр "-pid".)

Затем я назвал свою функцию "wait" и снова вызвал "ps", чтобы убедиться, что у зомби нет:

> wait()
list()
> system(paste0('ps --pid=', paste(pids, collapse=',')))
  PID TTY          TIME CMD

Похоже, что рабочие процессы, созданные mclapply, теперь исчезли. Это должно работать до тех пор, пока процессы были созданы текущим процессом R.