如何用单SBATCH脚本动态调节点数测试分布式算法可扩展性
我需要测试分布式算法的可扩展性,在选定集群上运行测试时,希望通过单个SBATCH脚本动态设置节点数量,同时避免以下情况:
- 申请N个节点但仅使用n<N个节点运行部分测试(浪费计算时长)
- 存在多个重叠的SBATCH请求
- 仅用单个SBATCH脚本完成所有测试
我拟提交如下脚本:
#!/bin/bash #SBATCH -p <req_partition> # Partition name #SBATCH --gres=gpu:<no_gpu> # No GPUs requested #SBATCH -N 128 # Number of nodes #SBATCH -n 128 # Total number of tasks #SBATCH --time=06:00:00 # Time limit #SBATCH --exclusive # Exclusive access to nodes #SBATCH --account=<account name> # Account name /run_tests.sh 128 # run the test suite on 128 node #SBATCH -N 64 # Number of nodes #SBATCH -n 64 # Total number of tasks /run_tests.sh 64 # run the test suite on 64 node #SBATCH -N 32 # Number of nodes #SBATCH -n 32 # Total number of tasks /run_tests.sh 32 # run the test suite on 32 node #SBATCH -N 16 # Number of nodes #SBATCH -n 16 # Total number of tasks /run_tests.sh 16 # run the test suite on 16 node
除了该脚本的可行性之外,我还存在疑问:减少节点数量的操作是否会造成阻塞?即能否确保释放部分节点后再运行下一轮测试,还是会在测试运行过程中释放节点导致测试结果失效?
脚本可行性分析
你写的这个脚本完全不可行。SBATCH指令只有在脚本提交到Slurm时才会被解析,脚本运行过程中后续的#SBATCH注释根本不会生效。也就是说,整个脚本只会用最开头申请的128节点、128任务配置跑完所有测试,后面的#SBATCH -N 64之类的指令等于白写,会直接造成大量计算资源浪费,完全违背你的初衷。
正确的实现方式
要在单个脚本里按顺序跑不同节点数的测试,同时动态释放节点避免浪费,有两种可靠方案:
方案1:用scontrol动态调整作业资源
如果你的集群支持Slurm的作业资源调整功能,可以直接修改当前作业的节点配置,步骤如下:
#!/bin/bash #SBATCH -p <req_partition> #SBATCH --gres=gpu:<no_gpu> #SBATCH -N 128 #SBATCH -n 128 #SBATCH --time=06:00:00 #SBATCH --exclusive #SBATCH --account=<account name> # 先跑128节点测试 /run_tests.sh 128 # 调整作业到64节点,触发Slurm释放多余节点 scontrol update jobid=$SLURM_JOB_ID NumNodes=64 NumTasks=64 # 短暂等待资源调整生效(部分集群需要这个步骤) sleep 30 # 跑64节点测试 /run_tests.sh 64 # 调整到32节点 scontrol update jobid=$SLURM_JOB_ID NumNodes=32 NumTasks=32 sleep 30 /run_tests.sh 32 # 调整到16节点 scontrol update jobid=$SLURM_JOB_ID NumNodes=16 NumTasks=16 sleep 30 /run_tests.sh 16
注意:
- 要确认你的集群启用了
JobResize功能,且所在分区允许调整节点数; - 脚本会先跑完当前测试,再执行资源调整,绝对不会在测试运行中释放节点,不会导致测试失效。
方案2:串行提交子作业
如果集群不支持动态调整资源,你可以用主脚本按顺序提交独立的子作业,通过依赖关系确保前一个测试完成后再启动下一个:
#!/bin/bash # 主脚本不需要申请资源,仅用于串行提交子任务 # 提交128节点测试,记录作业ID JOB_ID=$(sbatch --parsable -p <req_partition> --gres=gpu:<no_gpu> -N 128 -n 128 --time=06:00:00 --exclusive --account=<account name> /run_tests.sh 128) # 提交64节点测试,依赖前一个作业成功完成 JOB_ID=$(sbatch --parsable --dependency=afterok:$JOB_ID -p <req_partition> --gres=gpu:<no_gpu> -N 64 -n 64 --time=06:00:00 --exclusive --account=<account name> /run_tests.sh 64) # 提交32节点测试 JOB_ID=$(sbatch --parsable --dependency=afterok:$JOB_ID -p <req_partition> --gres=gpu:<no_gpu> -N 32 -n 32 --time=06:00:00 --exclusive --account=<account name> /run_tests.sh 32) # 提交16节点测试 sbatch --dependency=afterok:$JOB_ID -p <req_partition> --gres=gpu:<no_gpu> -N 16 -n 16 --time=06:00:00 --exclusive --account=<account name> /run_tests.sh 16
这种方式每个子作业独立申请对应数量的节点,前一个完成后才会启动下一个,完全避免资源浪费和重叠请求,也只用单个主脚本完成所有测试。
关于节点释放的疑问
用方案1的scontrol调整时,Slurm会等当前测试完全跑完,再执行节点释放操作,释放完成后才会继续运行下一轮测试。不会出现测试运行中释放节点的情况,也不会导致测试结果失效。scontrol update命令本身会立即返回,但集群后台调整资源需要一点时间,所以加个短sleep是稳妥的,确保节点配置完全生效后再启动下一轮测试。
内容的提问来源于stack exchange,提问作者Saverio Pasqualoni

