sbatchスクリプトの作成
目標
sbatchスクリプトを要件どおりに作成し、配列ジョブと依存関係のチェーンを構成して、#SBATCHの位置の誤りを検出する検証ツールを作ります。
なぜ重要なのか
#SBATCHは、最初の実行可能なコマンドが現れる前までしかパースされません。その後ろに書いたディレクティブはただのコメントになり、エラーも警告も出ません。そのため、GPUを要求したと思い込んでいるのにCUDAが見えないという状況が生まれ、ユーザーはコードを疑って時間を使います。このミスは人が目で見つけにくく、スクリプトなら簡単に見つけられます。
また、配列ジョブの%Nの制限を抜かすと、100個のジョブが一度にキューを埋めて他のユーザーを妨げます。共有クラスターでは、これは社会的な問題でもあります。
ステップ
/root/jobsディレクトリを作成し、/root/jobs/train.sbatchを作成してください。1行目は#!/bin/bashで、続けて--job-name=resnet-train、--output=/root/jobs/logs/%x-%j.out、--error=/root/jobs/logs/%x-%j.errの3つのディレクティブを入れてください。/root/jobs/logsディレクトリも作成してください。- 同じファイルにリソースの要求を追加してください。
--nodes=1、--ntasks=1、--cpus-per-task=8、--mem=64G - 時間とパーティションを追加してください。
--time=04:00:00、--partition=gpu - GPUの要求を追加してください。
--gres=gpu:a100:2 /root/jobs/sweep.sbatchを作成してください。配列ジョブで、--array=1-20%4と--output=/root/jobs/logs/%A_%a.outを含める必要があり、本文でSLURM_ARRAY_TASK_IDを使う必要があります。train.sbatchの本文に次を入れてください。set -euo pipefailで始まり、SLURM_CPUS_PER_TASK環境変数を使い、srunで実行する行が必要です。/root/jobs/pipeline.shを作成してください。--parsableで最初のジョブIDを受け取って変数に入れる行と、--dependency=afterok:で2つ目のジョブを投入する行の、2行が必要です。/root/jobs/lint.shを作成してください。最初の引数でsbatchスクリプトのパスを受け取り、次を検査します。違反がなければ終了コード0、あれば0以外の値で終了する必要があります。- ルール1: 1行目が
#!で始まっていること - ルール2: すべての
#SBATCH行が、最初の実行コマンド(コメントでも空行でもない行)より前にあること - ルール3:
--job-nameと--timeのディレクティブがあること 採点ツールが、あなたのtrain.sbatch(通過する必要があります)と、ディレクティブをわざと後ろに置いた誤ったスクリプト(失敗する必要があります)の両方に対して実行します。
- ルール1: 1行目が
参考
- 出力パターンの置換指定子:
%jはジョブID、%xはジョブ名、%Aは配列の親ID、%aは配列のインデックスです。 - 依存関係の例:
J1=$(sbatch --parsable prep.sh)の次の行にsbatch --dependency=afterok:$J1 train.sh - このラボで実際にsbatchを実行することはありません。スクリプトの内容を採点します。
- よくある間違い1: ディレクティブを
#SBATCH--timeのように空白なしで書く場合です。#SBATCHの後ろには空白が必要です。 - よくある間違い2: ステップ8の検証ツールが、空行やコメントを「実行コマンド」として数えてしまい、正常なスクリプトを失敗と判定する場合です。
基本の骨格
/root/jobsディレクトリを作成し、/root/jobs/train.sbatchを作成してください。1行目は#!/bin/bashで、続けて--job-name=resnet-train、--output=/root/jobs/logs/%x-%j.out、--error=/root/jobs/logs/%x-%j.errの3つのディレクティブを入れてください。/root/jobs/logsディレクトリも作成してください。
shebangの次の行からディレクティブを書きます。名前と、出力・エラーのパスが基本です。
リソースの要求
同じファイルにリソースの要求を追加してください。--nodes=1、--ntasks=1、--cpus-per-task=8、--mem=64G
ノード、タスク、タスクあたりのCPU、メモリの4つを指定します。
時間とパーティション
時間とパーティションを追加してください。--time=04:00:00、--partition=gpu
時間の形式は時:分:秒です。パーティションは、前のラボで作成したものを使ってください。
GPUの要求
GPUの要求を追加してください。--gres=gpu:a100:2
GRESの形式は名前:タイプ:個数です。前のラボで定義したタイプを使ってください。
配列ジョブ
/root/jobs/sweep.sbatchを作成してください。配列ジョブで、--array=1-20%4と--output=/root/jobs/logs/%A_%a.outを含める必要があり、本文でSLURM_ARRAY_TASK_IDを使う必要があります。
範囲の後ろにパーセントを付けて、同時に実行する数を制限できます。出力パターンには、配列の置換指定子を入れてください。
環境変数とsrun
train.sbatchの本文に次を入れてください。set -euo pipefailで始まり、SLURM_CPUS_PER_TASK環境変数を使い、srunで実行する行が必要です。
タスクあたりのCPU数を環境変数で読み取って使うと、リソースの要求とコードが自動で一致します。
依存関係のチェーン
/root/jobs/pipeline.shを作成してください。--parsableで最初のジョブIDを受け取って変数に入れる行と、--dependency=afterok:で2つ目のジョブを投入する行の、2行が必要です。
ジョブIDだけを出力するオプションがあります。成功したときだけ続くようにしてください。
スクリプトの検証ツール
/root/jobs/lint.shを作成してください。最初の引数でsbatchスクリプトのパスを受け取り、次を検査します。違反がなければ終了コード0、あれば0以外の値で終了する必要があります。
- ルール1: 1行目が
#!で始まっていること - ルール2: すべての
#SBATCH行が、最初の実行コマンド(コメントでも空行でもない行)より前にあること - ルール3:
--job-nameと--timeのディレクティブがあること 採点ツールが、あなたのtrain.sbatch(通過する必要があります)と、ディレクティブをわざと後ろに置いた誤ったスクリプト(失敗する必要があります)の両方に対して実行します。
最初の実行コマンドより後ろのディレクティブは無視されます。それを見つけ出すのが、この検証ツールの目的です。