我从多个并行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)
(上面,我不是让parSapply
将ff
的70个元素分配给各个进程,而是在创建批处理文件时手动拆分它们,然后并行运行批处理文件。)< / p>
答案 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)
您也可以删除已创建的目录,但由于使用了符号链接,它们不会占用太多空间,因此保留它们可能会更好,特别是在调试/测试期间。