如何使用 Slurm 为计算树建模?

How to model a tree of computations with Slurm?

我有一个包含 N 个步骤的模拟,运行 按顺序进行。这些步骤中的每一步都会修改内存中的全局状态,直到最后一步即结果。在一个步骤有 运行 之后,可以将此步骤刚刚计算的中间状态写入磁盘,并加载这样一个中间状态,而不是从头开始。写入和加载中间状态的成本不可忽略。

我想 运行 在 Slurm 集群上进行多种模拟。每个变化都会改变一些步骤的参数。

例子

模拟步骤

S1 --> S2 --> S3 --> S4

变化

run1: S2.speed=2, S3.height=12
run2: S2.speed=2, S3.height=20
run3: S2.speed=2, S3.height=40
run4: S2.speed=5, S3.height=12
run5: S2.speed=5, S3.height=80

我想做的是通过转储共享步骤的中间状态,让各种 运行 共享公共计算。这将形成一个步骤 运行s:

的树
S1
├─ S2 (speed=2)
│  ├─ S3 (height=12)
│  │  └─ S4
│  ├─ S3 (height=20)
│  │  └─ S4
│  └─ S3 (height=40)
│     └─ S4
└─ S2 (speed=5)
   ├─ S3 (height=12)
   │  └─ S4
   └─ S3 (height=80)
      └─ S4

我知道我可以通过 运行ning 5 个进程获得 5 运行 的结果:

run1: S1 --> S2 (speed=2) --> S3 (height=12) --> S4
run2: (dump of run1.S2) --> S3 (height=20) --> S4
run3: (dump of run1.S2) --> S3 (height=40) --> S4
run4: (dump of run1.S1) --> S2 (speed=5) --> S3 (height=12) --> S4
run5: (dump of run4.S2) --> S3 (height=80) --> S4

这将计算从使用简单方法的 20 个步骤减少到 3 个转储和 4 个加载的 13 个步骤。

现在,我的问题是如何使用 Slurm 对其进行建模,以充分利用调度程序?

我能想到的一个解决方案是,在中间状态转储之后,每个 运行 负责提交依赖于它的 运行 的作业。 run1转储S1后会提交运行4,转储S2后会提交运行2和运行3,运行4会提交运行5倾倒 S2 后。使用此解决方案,在将作业提交到 Slurm 时声明依赖关系是否有意义?

我看到的另一个解决方案是打破多个依赖作业中的长计算链。要提交的作业列表及其依赖项基本上就是我在上面绘制的树(除了 S3/S4 对将合并到同一个作业中)。这是要提交的 8 个作业,而不是 5 个,但我可以从一开始就一次提交所有作业,并具有正确的依赖关系。但是,我不确定这种方法的优点是什么。如果 Slurm 从一开始就知道完整的作业列表及其依赖项,那么它作为调度程序会做得更好吗?从用户的角度来看,提交所有作业并与依赖项链接(例如,取消所有依赖于根作业的作业)是否有一些优势?我知道我可以使用作业数组一次提交多个作业,但我看不到一种方法来声明同一数组的作业之间的依赖关系。是否可能,甚至是可取的?

最后,还有没有其他我没有想到的方法?

编辑

我举的例子当然简化了很多。真正的模拟将包含数百个步骤,并尝试大约一千种变化。所选解决方案的可扩展性很重要。

One solution I can think of, is that each run is responsible to submit the jobs of the runs that depend on it, after the dump of the intermediate state. With this solution, is there any point in declaring the dependency when submitting the job to Slurm?

这是一种通常用于简单工作流的方法,这些工作流涉及必须检查点并重新启动的长时间 运行 作业。

Another solution I can see is to break the long chains of computation in multiple, dependent jobs. Will Slurm do a better job as a scheduler, if he knows the full list of jobs and their dependencies right from the start?

没有。 Slurm 将忽略不符合启动条件的作业,因为它们的依赖作业尚未完成。

Are there some advantages from a user point of view, to have all the jobs submitted and linked with dependencies (eg, to cancel all the jobs that depend on the root job)?

是的,但这有点用处。

I know I can submit many jobs at once with a job array, but I don't see a way to declare dependencies between jobs of the same array. Is is possible, or even advisable?

不可以,您不能在同一数组的作业之间设置依赖关系。

Finally, are there other approaches I did not think about?

你可以使用 workflow management system.

最简单的解决方案之一是 Makeflow。它使用看起来像经典 Makefiles 的文件来描述作业之间的依赖关系。然后,简单地 运行 类似 makeflow –T slurm makefile.mf

另一种选择是Bosco。 Bosco 提供了更多的可能性,适合个人使用。它易于设置并且可以将作业提交到多个集群。

最后,Fireworks 是一个非常强大的解决方案。它需要 MongoDB,更适合实验室使用,但它可以根据作业的输出为作业 submission/resubmission 实现非常复杂的逻辑,并且可以巧妙地处理错误。例如,您可以实施一个工作流,在该工作流中提交具有给定参数的给定值的作业,并让 Fireworks 根据输出文件监视收敛,并在收敛不理想时取消并使用另一个值重新提交。

另一种可能的解决方案是使用管道工具。在生物信息学领域 SnakeMake 正变得非常流行。 SnakeMake 基于 GNU Make,但在 Python 中制作,因此得名 SnakeMake。为了让 SnakeMake 工作,你指定你想要的输出,SnakeMake 将推断出它必须 rules 到 运行 这个输出。 SnakeMake 的优点之一是它可以很容易地从个人笔记本电脑扩展到更大的计算机,甚至集群(例如 slurm 集群)。你的例子看起来像这样:

rule all:
    input:
        ['S4_speed_2_height_12.out',
         'S4_speed_2_height_20.out',
         'S4_speed_2_height_40.out',
         'S4_speed_5_height_12.out',
         'S4_speed_5_height_80.out']

rule S1:
    output:
        "S1.out"
    shell:
        "touch {output}"  # do your heavy computations here

rule S2:
    input:
        "S1.out"
    output:
        "S2_speed_{speed}.out"
    shell:
        "touch {output}"

rule S3:
    input:
        "S2_speed_{speed}.out"
    output:
        "S3_speed_{speed}_height_{height}.out"
    shell:
        "touch {output}"

rule S4:
    input:
        "S3_speed_{speed}_height_{height}.out"
    output:
        "S4_speed_{speed}_height_{height}.out"
    shell:
        "touch {output}"

然后我们可以要求 snakemake 制作一个漂亮的图像来说明它是如何执行这些计算的:

Snakemake 会自动找出不同规则可以使用的输出。

运行 这在您的本地计算机上就像执行 snakemake 一样简单,而将操作提交到 slurm 只是 snakemake --cluster "sbatch"。我给出的例子显然过于简单化了,但是 SnakeMake 是高度可定制的(每个规则的线程数、内存使用量等),并且具有基于 Python 的优点。弄清楚 SnakeMake 中的一切是如何工作的需要一点时间,但我绝对可以推荐它。