继续this question (https://stackoverflow.com/questions/17222942/allow-foreach-workers-to-register-and-distribute-sub-tasks-to-other-workers),将doSNOW和SOCK集群连接到Torque / MOAB调度程序的最佳做法是什么,以避免在处理外部并行代码的某些部分的内部并行循环中处理器关联循环?
从the Steve's answer to that question开始,没有调度程序的基线代码可以是:
library(doSNOW)
hosts <- c('host-1', 'host-2')
cl <- makeSOCKcluster(hosts)
registerDoSNOW(cl)
r <- foreach(i=1:4, .packages='doMC') %dopar% {
registerDoMC(2)
foreach(j=1:8, .combine='c') %dopar% {
i * j
}
}
stopCluster(cl)
答案 0 :(得分:3)
Torque始终会创建一个文件,其中包含Moab已分配给您的作业的节点名称,并通过PBS_NODEFILE
环境变量将该文件的路径传递给您的作业。可以多次列出节点名称,以指示它为该节点上的作业分配了多个核心。在这种情况下,我们希望为PBS_NODEFILE
中的每个唯一节点名称启动集群工作程序,但要跟踪每个节点上已分配核心的数量,以便我们在注册时指定正确的核心数{{ 1}}。
这是一个读取doMC
并返回带有分配的节点信息的数据帧的函数:
PBS_NODEFILE
返回的数据框包含一个名为“x”的节点名称列和一个名为“Freq”的列,其中包含相应的核心数。
这使得创建和注册SOCK集群变得简单,每个唯一节点有一个工作者:
getnodes <- function() {
f <- Sys.getenv('PBS_NODEFILE')
x <- if (nzchar(f)) readLines(f) else rep('localhost', 3)
as.data.frame(table(x), stringsAsFactors=FALSE)
}
我们现在可以轻松地执行一个nodes <- getnodes()
cl <- makeSOCKcluster(nodes$x)
registerDoSNOW(cl)
循环,每个工作一个任务,但是如果不依赖于{{1}的某些实现细节,将正确数量的已分配核心传递给每个工作者并不容易。 }和foreach
,具体与snow
使用的doSNOW
函数的实现有关。当然,如果您碰巧知道每个节点上分配的核心数量相同,那么很容易,但如果您想要解决问题的一般解决方案,则会更难。
一个(不那么优雅)通用解决方案是通过snow clusterApplyLB
函数将分配的核心数分配给每个工作人员的全局变量:
doSNOW
这可以保证每个worker上的“allocated.cores”变量的值等于该节点在clusterApply
中出现的次数。
现在我们可以在注册setcores <- function(cl, nodes) {
f <- function(cores) assign('allocated.cores', cores, pos=.GlobalEnv)
clusterApply(cl, nodes$Freq, f)
}
setcores(cl, nodes)
时使用该全局变量:
PBS_NODEFILE
以下是可用于执行此R脚本的示例作业脚本:
doMC
当通过r <- foreach(i=seq_along(nodes$x), .packages='doMC') %dopar% {
registerDoMC(allocated.cores)
foreach(j=1:allocated.cores, .combine='c') %dopar% {
i * j
}
}
命令提交时,R脚本将创建一个包含四个工作程序的SOCK集群,并且每个工作程序将使用8个核心执行内部#!/bin/sh
#PBS -l nodes=4:ppn=8
cd "$PBS_O_WORKDIR"
R --slave -f hybridSOCK.R
循环。但由于R代码是通用的,无论通过qsub
请求的资源如何,都应该做正确的事。