Checkpointing¶
Slurm signals a job when it is preempted, and slurm_signal_delay_s seconds (default 90) before its time limit. submitit then requeues it:
a callable without
__submitit_checkpoint__method restarts from scratch after a preemption, and ends with aFailedJobErrorat its time limit;a callable with it is requeued with the submission this method returns, after a preemption or each of its first
slurm_max_num_timeouttimeouts (default 3, set withsubmitit.AutoExecutor(folder, slurm_max_num_timeout=...)).
A signal received after min(0.8 x limit, limit - 10 minutes) of runtime counts as a timeout.
Only the first task of a multi-task job calls __submitit_checkpoint__.
Preemptions are signaled only if the cluster sets SlurmctldParameters=preempt_send_user_signal (check with scontrol show config | grep SlurmctldParameters).
Processes forked by the job ignore SIGTERM, see the FAQ.
Resume from the current state¶
submitit.helpers.Checkpointable implements __submitit_checkpoint__ by requeuing the callable in its current state, with the same arguments:
import time
import submitit
class Counter(submitit.helpers.Checkpointable):
def __init__(self) -> None:
self.count = 0 # saved when the job is requeued
def __call__(self, until: int) -> int:
while self.count < until: # resumes from the saved count
time.sleep(1)
self.count += 1
return self.count
executor = submitit.AutoExecutor(folder="logs/%j")
executor.update_parameters(timeout_min=60, slurm_partition="<your_partition>")
job = executor.submit(Counter(), 7200)
See also submitit.helpers.FunctionSequence, which runs several calls in one job and does not rerun finished calls after requeuing.
Customize the resubmission¶
__submitit_checkpoint__ receives the arguments of __call__ and returns a submitit.helpers.DelayedSubmission, built like executor.submit arguments, or None to end the job (job.result() then raises a FailedJobError).
For instance, to save a model to a file rather than pickling it with the callable:
from pathlib import Path
import submitit
class Training:
def __init__(self) -> None:
self.model = None
def __call__(self, checkpoint_path: str) -> None:
if Path(checkpoint_path).exists():
self.model = ... # load the model from checkpoint_path
else:
self.model = ... # initialize the model
... # train the model
def __submitit_checkpoint__(self, checkpoint_path: str) -> submitit.helpers.DelayedSubmission:
... # save self.model to checkpoint_path
return submitit.helpers.DelayedSubmission(Training(), checkpoint_path)
job = executor.submit(Training(), "checkpoint.pt")
Test checkpointing¶
On Slurm,
job._interrupt()simulates a preemption, andjob._interrupt(timeout=True)a timeout.Locally, run the job with
cluster="local"andtimeout_min=1: the local executor requeues timed-out jobs the same way.