doParallel worker 的输出

output of doParallel workers

假设我有一段代码在 foreach 循环中进行各种计算,为简单起见,可以这样写:

foreach( idx = 1:10 ) %dopar% {
   set.seed(idx)
   smp <- rnorm(10)
   print( sprintf('Index: %d', idx) )
   print( cat(sprintf('Value: %f \n', smp[1:10])) )
}

代码的主要思想是它的输出是以这样的方式编写的,索引显示一次,之后是主代码的各种结果。使用 %do% 时,可以很容易地读取日志文件并使用这种结构进行调试,但是使用 %dopar% 时,日志文件会变得混乱。

问题:在不更改代码的情况下,有没有办法让每个工人创建自己的输出文件?我是使用 doParallel 的新手,所以到目前为止我发现的并行输出的唯一方法是使用以下内容:

library(doParallel)
cl <- makeCluster(4, outfile = 'log.txt')
registerDoParallel(cl)

但是,outfile 只接受一个字符串作为参数,还没有找到将名称向量传递给它的方法。那么,也许有一种方法可以为每个工人指定一个输出文件?

注意:正在处理 Windows 7.

许多解决方案——比如不在 worker 中使用 print 或在那里指定输出文件——浮现在脑海中,但问题明确要求 不更改代码 .因此,我假设当前在 %dopar% 正文中的代码包含在一个无法更改的函数中:

doNotTouch <- function(idx) {
  set.seed(idx)
  smp <- rnorm(10)
  print(sprintf('Index: %d', idx))
  cat(sprintf('Value: %f \n', smp[1:10]))
}

一个解决方案是直接指定输出文件 before doNotTouch 被调用(或者更准确地说:在输出被重定向到一个环境中执行函数某个文件):

library(doParallel)
cl <- makeCluster(4)
registerDoParallel(cl)


foreach(idx = 1:10) %dopar% {
  capture.output(
    doNotTouch(idx), 
    file = paste0("log", idx, ".txt"))
}

stopCluster(cl)

这不会更改问题中提供的代码,而是将其放在一个包装器中,该包装器负责处理正确的输出文件 log_[idx].txt

编辑:在评论中,澄清了问题不是关于写入单独的文件,而是关于获得不混乱的输出。在这种情况下,可以在循环中使用 return(capture.output(doNotTouch(idx))) 来收集属于一起的输出并立即返回它们。此外,@Nutle 发现 Sys.getpid() returns 工人的 PID 可用于写入单独的日志文件 by worker(与 by 相对迭代).