You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在tcsh中通过for循环提交带参数的分批次并行Python SLURM作业

问题需求与背景
  • 需分批次运行Python脚本:先并行执行4个仅第二个参数不同的实例,待这批全部完成后,再运行修改第一个参数的下一批实例(后续批次依赖前一批生成的HDF5文件)
  • 必须使用tcsh执行,以继承.cshrc中配置的环境变量和加载的模块
  • 单脚本运行时长接近集群作业时限,无法用Python内部循环实现批量
  • 此前尝试的tcsh脚本因语法错误无法正常运行,原脚本问题点包括:变量赋值带空格、变量拼接解析错误

原报错tcsh脚本:

#!/bin/tcsh
#SBATCH --job-name=looptest    ## Name of the job
#SBATCH --output=looptest.out  ## Output file
#SBATCH --get-user-env
OUTPUT = file  # tcsh赋值不能有空格,语法错误

for i in `seq 1 3`; do
  for j in `seq 1 3`; do
    srun \
      -N1 \
      --cpus-per-task=48 \
      ./slurmtest.py $i $j > "$OUTPUT_$i_$j.out" &  # 变量拼接边界不明确,解析错误
  done
done

wait

Python脚本核心逻辑(依赖前批次输出):

import os
import sys

# 注意:需添加类型转换,否则字符串拼接会出错
continue_from_number = int(sys.argv[1])
folder_name = 'folder_' + str(sys.argv[2]) + '/'

oname = None
for filename in os.listdir(folder_name):
    if filename.startswith(f"output_{continue_from_number}_"):
        oname = filename
        break

# 处理oname,生成带str(continue_from_number + 1)的输出文件

解决方案

根据集群作业时限的不同,提供两种可行方案:

1. 单SLURM作业内分批次并行(适合单批次总时长未超集群时限)

修正tcsh语法错误,用原生循环实现批次内并行、批次间等待:

#!/bin/tcsh
#SBATCH --job-name=batch_run
#SBATCH --output=batch_run_%j.out
#SBATCH --get-user-env
#SBATCH --nodes=1
#SBATCH --cpus-per-task=48  # 按单任务实际资源需求调整

# tcsh变量赋值语法:无空格,用set命令
set OUTPUT_PREFIX="output"

# 批次循环:第一个参数(continue_from_number)列表,按需调整
foreach continue_num (1 2 3)
    echo "Starting batch: continue_num = $continue_num"
    # 本批次内并行执行4个任务(第二个参数folder_num不同)
    foreach folder_num (1 2 3 4)
        # 用${}明确变量边界,避免解析错误
        srun \
            --exclusive \  # 避免子任务共享资源,按需启用
            -N1 \
            --cpus-per-task=48 \
            ./script.py $continue_num $folder_num > "${OUTPUT_PREFIX}_${continue_num}_${folder_num}.out" &
    end
    # 等待本批次所有后台任务完成,再进入下一批次
    wait
    echo "Batch completed: continue_num = $continue_num"
end

关键修正点

  • tcsh变量赋值:set OUTPUT_PREFIX="output"(无空格)
  • 变量拼接:${OUTPUT_PREFIX}_${continue_num}_${folder_num}.out(用${}明确变量边界)
  • 用tcsh原生foreach循环替代for in,兼容性更好
  • 每批次内用&后台启动任务,wait阻塞至本批次全部完成

2. 独立SLURM作业+依赖控制(适合单任务时长接近集群时限)

如果单任务时长接近集群单作业上限,需提交独立作业,通过SLURM依赖语法实现批次同步:

第一步:编写单任务执行脚本run_single_task.csh

#!/bin/tcsh
#SBATCH --get-user-env
#SBATCH --nodes=1
#SBATCH --cpus-per-task=48
# 接收外部传入的两个参数
set continue_num = $1
set folder_num = $2
./script.py $continue_num $folder_num > "output_${continue_num}_${folder_num}.out"

第二步:编写批次提交脚本submit_batches.csh

#!/bin/tcsh
#SBATCH --job-name=submit_batches
#SBATCH --output=submit_batches.out
#SBATCH --get-user-env

set continue_nums = (1 2 3)  # 批次列表
set prev_job_ids = ""

foreach continue_num ($continue_nums)
    set curr_job_ids = ""
    echo "Submitting batch: continue_num = $continue_num"
    
    # 提交本批次的4个任务
    foreach folder_num (1 2 3 4)
        # --parsable参数直接返回作业ID,便于后续拼接依赖
        set job_id = `sbatch --parsable \
            --job-name=task_${continue_num}_${folder_num} \
            --dependency=afterok:$prev_job_ids \  # 非第一批时,依赖上一批所有作业成功完成
            ./run_single_task.csh $continue_num $folder_num`
        set curr_job_ids = "${curr_job_ids}:${job_id}"
    end
    
    # 去掉job_ids开头的冒号,更新上一批作业ID列表
    set prev_job_ids = `echo $curr_job_ids | sed 's/^://'`
end

关于SLURM依赖的说明

  • --dependency=afterok:<jobid1>:<jobid2>是实现跨批次同步的可靠方式,仅当指定的所有作业成功完成(退出码为0),当前作业才会启动
  • 若使用单SLURM作业内的srun+wait,则无需依赖语法,wait会自动阻塞至本批次任务全部完成

额外注意事项

  • 确保Python脚本中对sys.argv[1]做整数转换,避免字符串拼接逻辑错误
  • 根据集群资源限制调整--cpus-per-task、--nodes等参数,避免资源超限
  • 测试时可缩小循环范围(如仅跑1个批次、2个任务),验证逻辑正确后再批量运行

内容的提问来源于stack exchange,提问作者Ferenc Lengyel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:40:42