Как создать индикатор выполнения при использовании функции "foreach()" в R?

есть некоторые информационные сообщения о том, как создать счетчик для циклов в программе R. Однако как вы создаете аналогичную функцию при использовании параллельной версии с "foreach()"?

Ответ 1

Изменить: после update в пакет doSNOW стало довольно просто отображать хороший индикатор выполнения при использовании %dopar%, и он работает на Linux, Windows и OS X

doSNOW теперь официально поддерживает индикаторы выполнения через аргумент .options.snow.

library(doSNOW)
cl <- makeCluster(2)
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(max = iterations, style = 3)
progress <- function(n) setTxtProgressBar(pb, n)
opts <- list(progress = progress)
result <- foreach(i = 1:iterations, .combine = rbind, 
                  .options.snow = opts) %dopar%
{
    s <- summary(rnorm(1e6))[3]
    return(s)
}
close(pb)
stopCluster(cl) 

Еще один способ отслеживания прогресса, если вы помните об общем количестве итераций, заключается в установке .verbose = T, поскольку это будет печатать на консоли, итерации которой завершены.

Предыдущее решение для Linux и OS X

В Ubuntu 14.04 (64 бит) и OS X (El Capitan) индикатор выполнения отображается даже при использовании %dopar%, если в makeCluster функция oufile = "" установлена. Кажется, он не работает под Windows. С помощью makeCluster:

outfile: где направлять выходные сообщения stdout и stderr от рабочих. "" указывает на отсутствие перенаправления (что может быть полезно только для рабочих на локальной машине). По умолчанию используется '/dev/null (' nul: в Windows).

Пример кода:

library(foreach)
library(doSNOW)
cl <- makeCluster(4, outfile="") # number of cores. Notice 'outfile'
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(min = 1, max = iterations, style = 3)
result <- foreach(i = 1:iterations, .combine = rbind) %dopar% 
{
      s <- summary(rnorm(1e6))[3]
      setTxtProgressBar(pb, i) 
      return(s)
}
close(pb)
stopCluster(cl) 

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

Ответ 2

Этот код является модифицированной версией примера doRedis и будет создавать индикатор выполнения даже при использовании %dopar% с параллельным бэкэнд:

#Load Libraries
library(foreach)
library(utils)
library(iterators)
library(doParallel)
library(snow)

#Choose number of iterations
n <- 1000

#Progress combine function
f <- function(){
  pb <- txtProgressBar(min=1, max=n-1,style=3)
  count <- 0
  function(...) {
    count <<- count + length(list(...)) - 1
    setTxtProgressBar(pb,count)
    Sys.sleep(0.01)
    flush.console()
    c(...)
  }
}

#Start a cluster
cl <- makeCluster(4, type='SOCK')
registerDoParallel(cl)

# Run the loop in parallel
k <- foreach(i = icount(n), .final=sum, .combine=f()) %dopar% {
  log2(i)
}

head(k)

#Stop the cluster
stopCluster(cl)

Вы должны знать количество итераций и функцию комбинации загодя.

Ответ 3

Теперь это возможно с пакетом parallel. Протестировано с помощью R 3.2.3 на OSX 10.11, работающим внутри RStudio, с использованием кластера "PSOCK".

library(doParallel)

# default cluster type on my machine is "PSOCK", YMMV with other types
cl <- parallel::makeCluster(4, outfile = "")
registerDoParallel(cl)

n <- 10000
pb <- txtProgressBar(0, n, style = 2)

invisible(foreach(i = icount(n)) %dopar% {
    setTxtProgressBar(pb, i)
})

stopCluster(cl)

Странно, он отображается только с помощью style = 3.

Ответ 4

Вы сохраняете время начала с Sys.time() до цикла. Перебирайте строки или столбцы или что-то общее. Затем внутри цикла вы можете вычислить время, прошедшее до сих пор (см. difftime), процент завершения, скорость и расчетное время. Каждый процесс может печатать эти строки прогресса с помощью функции message. Вы получите что-то вроде

1/1000 complete @ 1 items/s, ETA: 00:00:45
2/1000 complete @ 1 items/s, ETA: 00:00:44

Очевидно, что порядок циклов сильно повлияет на то, насколько хорошо это работает. Не знаю о foreach, но с multicore mclapply вы получите хорошие результаты, используя mc.preschedule=FALSE, что означает, что элементы распределяются на процессы поочередно, так как предыдущие элементы завершаются.

Ответ 5

Следующий код создаст хороший индикатор выполнения в R для структуры управления foreach. Он также будет работать с графическими индикаторами выполнения, заменив txtProgressBar на желаемый объект строки выполнения.

# Gives us the foreach control structure.
library(foreach)
# Gives us the progress bar object.
library(utils)
# Some number of iterations to process.
n <- 10000
# Create the progress bar.
pb <- txtProgressBar(min = 1, max = n, style=3)
# The foreach loop we are monitoring. This foreach loop will log2 all 
# the values from 1 to n and then sum the result. 
k <- foreach(i = icount(n), .final=sum, .combine=c) %do% {
    setTxtProgressBar(pb, i)
    log2(i)
}
# Close the progress bar.
close(pb)

В то время как приведенный выше код отвечает на ваш вопрос в его самой базовой форме, более сложный и более сложный вопрос: можете ли вы создать индикатор выполнения R, который контролирует ход выполнения инструкции foreach, когда она распараллеливается с% dopar%. К сожалению, я не думаю, что можно контролировать прогресс параллельного foreach таким образом, но я бы хотел, чтобы кто-то доказал мне свою ошибку, поскольку это будет очень полезная функция.