允许(或绕过)对文件的并行写访问

时间:2014-05-07 23:42:51

标签: r parallel-processing

我从多个并行R进程调用Windows可执行文件(通过system内的parSapply调用)。这个.exe(让它叫它my.exe)传递一个文件名作为参数,并处理这个文件(细节可能是无关紧要的)。不幸的是,my.exe创建了一个日志文件(与my.exe在同一目录中),它在运行时写入,并且,由于日志文件的名称是固定的,后续R进程调用my.exe导致my.exe`抛出错误:

Cannot create result file "log.res". 
Do you have write access in the current directory?

我设法通过创建my.exe的多个副本(与cluster中的核心数量一样多,即7)来解决这个问题。然后,我可以确保每个只在任何时间由单个R进程使用,方法是将7个路径的向量传递给核心.bat文件,每个路径重复调用my.exe的给定副本。

是否有更优雅的方式来处理此问题,可能是让流程自动创建my.exe的虚拟实例?我不需要日志文件。

由于这是程序而不是R抛出的错误,我怀疑可能无法允许从R端对日志文件进行并发写访问。

理想情况下,我想做这样的事情:

ff <- c('a', 'long', 'vector', 'of', 'file', 'paths') # abbreviated
parSapply(cl, ff, function(f) system(sprintf("my.exe %s", f)))

但相反,我(或多或少)采取了这种做法(将my.exe复制到c:/1/c:/2/c:/3/,再过c:/7/ ):

cat(paste('CALL C:/1/my.exe',  ff[1:10], '/RUN=YES'), file='run1.bat', sep='\n')
cat(paste('CALL C:/2/my.exe', ff[11:20], '/RUN=YES'), file='run2.bat', sep='\n')
cat(paste('CALL C:/3/my.exe', ff[21:30], '/RUN=YES'), file='run3.bat', sep='\n')
cat(paste('CALL C:/4/my.exe', ff[31:40], '/RUN=YES'), file='run4.bat', sep='\n')
cat(paste('CALL C:/5/my.exe', ff[41:50], '/RUN=YES'), file='run5.bat', sep='\n')
cat(paste('CALL C:/6/my.exe', ff[51:60], '/RUN=YES'), file='run6.bat', sep='\n')
cat(paste('CALL C:/7/my.exe', ff[61:70], '/RUN=YES'), file='run7.bat', sep='\n')
parSapply(cl, c('run1.bat', 'run2.bat', 'run3.bat', 'run4.bat',
                'run5.bat', 'run6.bat', 'run7.bat'), system)

(上面,我不是让parSapplyff的70个元素分配给各个进程,而是在创建批处理文件时手动拆分它们,然后并行运行批处理文件。)< / p>

1 个答案:

答案 0 :(得分:2)

听起来你的基本策略是问题的唯一已知解决方案,但我认为它可以更优雅地完成。例如,您可以通过让每个worker根据worker ID执行不同的命令行来避免创建.BAT文件。可以使用以下方式分配工作者ID:

# Assign worker ID's to each of the cluster workers
setid <- function(id) assign(".Worker.id", id, pos=globalenv())
clusterApply(cl, seq_along(cl), setid)

此外,您可能希望自动创建包含“my.exe”的目录。我也更喜欢使用符号链接而不是可执行文件的副本:

# Create directories containing a symlink to the real executable
exepath <- "C:/bin/my.exe"  # Path to the real executable
pdir <- getwd()  # Parent of the new executable directories
myexe <- file.path(pdir, sprintf("work_%d", seq_along(cl)), "my.exe")
for (x in myexe) {
  dir.create(dirname(x), showWarnings=FALSE)
  if (file.exists(x)) 
    unlink(x)
  file.symlink(exepath, x)
}

如果符号链接不会欺骗“my.exe”在所需目录中创建日志文件,则可以尝试使用“file.copy”而不是“file.symlink”。

现在您可以使用以下命令运行并行作业:

# Each worker executes a different symlink to the real executable
worker.fun <- function(f, myexe) {
  system(sprintf("%s %s /RUN=YES", myexe[.Worker.id], f))
}
ff <- c('a', 'long', 'vector', 'of', 'file', 'paths')
parSapply(cl, ff, worker.fun, myexe)

您也可以删除已创建的目录,但由于使用了符号链接,它们不会占用太多空间,因此保留它们可能会更好,特别是在调试/测试期间。