sbatchスクリプトの書き方
一言でいうと
sbatchスクリプトの#SBATCHの行はコメントのように見えますが、リソースの契約書です。そして、最初の実行コマンドより前にあって初めて読み取られます。
なぜ必要なのか
最もよくある初心者のミスがこれです。
#!/bin/bash
echo "starting"
#SBATCH --gres=gpu:2 # <- 무시된다!
python train.py
#SBATCHは、最初の実行可能なコマンドが現れる前までしかパースされません。それより後ろにあるものは、ただのコメントです。エラーも警告も出ません。ジョブはGPUなしで投入され、ユーザーはなぜCUDAが見えないのかとしばらく迷います。
どう動くのか
基本の骨格
#!/bin/bash
#SBATCH --job-name=resnet-train
#SBATCH --output=/home/me/logs/%x-%j.out
#SBATCH --error=/home/me/logs/%x-%j.err
#SBATCH --partition=gpu
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=8
#SBATCH --mem=64G
#SBATCH --gres=gpu:a100:2
#SBATCH --time=04:00:00
set -euo pipefail
echo "job $SLURM_JOB_ID on $SLURMD_NODENAME"
srun python train.py --epochs 50
出力ファイルのパターンの置換指定子を知っておくと便利です。
| パターン | 値 |
|---|---|
%j |
ジョブID |
%x |
ジョブ名 |
%A |
配列ジョブの親ID |
%a |
配列のインデックス |
%N |
最初のノード名 |
出力パスのディレクトリが事前に存在している必要があります。ないとジョブは開始直後に失敗し、その失敗理由を書き込むファイルも作れないため、原因がわかりにくくなります。
便利な環境変数
ジョブの中でSlurmが入れてくれる値です。
SLURM_JOB_ID 작업 ID
SLURM_JOB_NAME 작업 이름
SLURM_JOB_NODELIST 할당된 노드 목록
SLURMD_NODENAME 현재 실행 중인 노드
SLURM_CPUS_PER_TASK 태스크당 CPU <- DataLoader num_workers 에 쓰면 좋다
SLURM_ARRAY_TASK_ID 배열 인덱스
SLURM_NTASKS 태스크 총 개수
num_workers=int(os.environ.get("SLURM_CPUS_PER_TASK", 4))のように書くと、リソースの要求とコードが自動で一致します。
srunの役割
sbatchスクリプトの中でsrunを使うと、ジョブステップが作られます。複数のノードやタスクで並列実行するときに必要です。単一のプロセスなら、なくてもかまいませんが、あるとリソースのアカウンティングが正確になります。
srun --ntasks=4 python ddp_train.py # 4개 프로세스로
配列ジョブ
同じスクリプトを、パラメーターだけ変えて何度も実行するときに使います。
#SBATCH --array=1-100%10
1-100はインデックスの範囲、%10は同時に実行する最大数です。この制限がないと、100個が一度にキューに入って、他のユーザーを妨げます。
python sweep.py --config "configs/exp${SLURM_ARRAY_TASK_ID}.yaml"
依存関係
JOB1=$(sbatch --parsable prep.sh)
sbatch --dependency=afterok:$JOB1 train.sh
| 条件 | 意味 |
|---|---|
afterok:ID |
そのジョブが成功で終わったあと |
afterany:ID |
成功か失敗かを問わず、終わったあと |
afternotok:ID |
失敗で終わったあと(後始末のジョブ用) |
singleton |
同じ名前の自分のジョブがないとき |
--parsableはジョブIDだけを出力するので、変数に入れるのに向いています。
状態の確認
squeue -u $USER
squeue -j 12345 -o '%.10i %.20j %.8T %.10M %.6D %R'
scontrol show job 12345
sacct -j 12345 --format=JobID,JobName,State,Elapsed,MaxRSS,ReqTRES
scancel 12345
squeueの最後の列(%R)が待機理由です。Resources(リソース待ち)、Priority(優先度待ち)、Dependency(依存待ち)、QOSMaxJobsPerUserLimit(制限に達した)などが出ます。ジョブが動かない理由の半分は、この1列に書かれています。
現場での姿
--memを指定せず既定値で動く場合。クラスターの既定値が小さいとOOMで落ち、大きいと他のジョブが入れません。明示する習慣をつけるのがよいです。
--timeを最大値で書く文化。全員がそうすると、バックフィルが無力になって全体のスループットが下がります。実際に近い値を書いて20%ほど余裕を持たせるのが、お互いの利益になります。
次のラボですること
sbatchスクリプトを要件どおりに作成し、配列ジョブと依存関係のチェーンまで構成します。最後に、#SBATCHの位置の誤りを検出する検証スクリプトを自分で作ります。