如何跨多个 slurm 节点并行化 future_pmap()

How to parallelize future_pmap() across multiple slurm nodes

我可以访问具有许多节点的大型计算集群,每个节点都有 >16 个核心,运行ning Slurm 20.11.3。我想 运行 使用 furrr::future_pmap() 并行工作。我可以在单个节点上跨多个内核并行化,但我无法找出正确的语法来利用多个节点上的内核。看到这个 .

这是一个可重现的例子,我做了一个休眠 5 秒的函数,returns 开始时间、结束时间和节点名称。

library(furrr)

# Set up parallel processing 
options(mc.cores = 64)
plan(
    list(tweak(multicore, workers = 16),
         tweak(multicore, workers = 16),
         tweak(multicore, workers = 16),
         tweak(multicore, workers = 16))
)


fake_fn <- function(x) {
  t1 <- Sys.time()
  Sys.sleep(x)
  t2 <- Sys.time()
  hn <- system2('hostname', stdout = TRUE)
  data.frame(start=t1, end=t2, hostname=hn)
}

stuff <- data.frame(x = rep(5, 64))

output <- future_pmap_dfr(stuff, function(x) fake_fn(x))

我 运行 使用 salloc --nodes=4 --ntasks=64 和 运行 交互地使用上述 R 脚本的作业。

脚本 运行s 在大约 20 秒内并且 returns 所有行的主机名相同,表明它在一个节点上同时 运行ning 16 次迭代而不是 64 次迭代按预期同时拆分 4 个节点。我应该如何更改 plan() 语法以便我可以利用多个节点?

编辑:我还尝试了一些其他的东西:

options(mc.cores = 64)
plan(
    list(tweak(multicore, workers = 16),
         tweak(multicore, workers = 16),
         tweak(multicore, workers = 16),
         tweak(multicore, workers = 16))
)

抱歉,这不起作用。当您指定像这样的未来策略列表时,您就是在指定 nested 未来调用中应使用的内容。在您的 future_pmap_dfr() 示例中,将仅使用此列表中的第一级。其他三个级别从未使用过。有关详细信息,请参阅 https://future.futureverse.org/articles/future-3-topologies.html


I replaced ... with plan(cluster(workers = availableWorkers()) ...

是的,

plan(cluster, workers = availableWorkers())

相当于默认,

plan(cluster)

这里是正确的尝试。

... but it just hangs.

这里可能发生了两件事。第一个,就是工人一个一个的设置。所以,如果你有很多,plan() 完成之前需要很长时间。我建议您只与两名工人一起尝试,以确认它是否有效。您还可以打开调试输出以查看发生了什么,即

library(future)
options(parallelly.debug = TRUE)
plan(cluster)

其次,跨节点使用 PSOCK 集群需要您具有对这些并行工作程序的 SSH 访问权限。并非所有 HPC 环境都支持,例如他们可能会阻止用户 SSH:ing 进入计算节点。这也可能是您正在经历的。如上,打开调试,找出它停顿的地方。

现在,即使您成功实现了此功能,您也会面临 R 中的限制,即您最多只能拥有 125 个并行工作器,但通常会少一些。您可以在 https://github.com/HenrikBengtsson/Wishlist-for-R/issues/28 中阅读有关此限制的更多信息。它还表明可以调整 R 源代码并重新编译以将此限制增加到数千。


上述方法的替代方法是使用 future.batchtools;

plan(future.batchtools::batchtools_slurm, workers = availableCores())

这将导致 future_pmap_dfr() 中的任务将通过 n = availableCores() Slurm 作业解决。当然,这伴随着调度程序的额外开销,例如排队、启动、运行、完成和读回数据。

顺便说一句,讨论这些事情的最佳地点是 https://github.com/HenrikBengtsson/future/discussions