Longs jobs, arrêt et reprise des calculs : idempotent, checkpoint, DMTCP.

1. Avant-propos

L’objectif de cette fiche est de présenter quelles sont les méthodes qui peuvent «assurer» l’exécution de très longs travaux sur la plateforme de calcul. Si un programme n’a pas «nativement» un procédé d’avancement en son sein, tout arrêt brutal implique une reprise à partir de zéro.

Pour éviter cela, il n’y a parfois pas d’autres solutions que d’effectuer un checkpoint. Soit l’utilisateur le met en place dans son programme, c’est l’idéal, mais la mise en œuvre peut être complexe. Soit on utilise un programme externe qui le fait, c’est précisément le second objectif de cette fiche : présenter DMTCP (Distributed MultiThreaded CheckPointing). En lançant ses travaux conjointement avec DMTCP , un utilisateur peut générer un ou plusieurs “checkpoint” de son job en cours d’exécution. Les checkpoints sont soit décidés au lancement par une instruction périodique, soit déclenchés par l’envoi/réception d’un signal.

Les principes ainsi que la mise en œuvre de tels procédés ne sont pas triviaux. Avant d’utiliser cette méthode, il est donc essentiel de s’assurer que l’on en a réellement besoin et surtout, qu’il n’existe pas une solution plus simple, mieux adaptée.

1.1. Quelques exemples d’arrêt et reprise de jobs où la méthode dmtcp est parfaitement inutile

D’une manière générale la plupart des programmes font des calculs en parallèle (et non un calcul parallèle!) :

gestion manuelle : si nativement le programme «gère» son avancé. ex: traitement massif : « tant qu’il y a du grain à moudre dans “IN”, traiter et placer les résulats dans “OUT”» :

le même programme lancé de très nombreuses fois avec des paramètres différents : La solution pour ce type travaux est de soumettre un job de type array dans la queue besteffort avec l’option idempotent: tous les jobs du array qui auraient été stoppés - par exemple en raison d’une affluence d’une série de jobs de priorité supérieure (default queue)- reprennent automatiquement , dès que suffisamment de ressources (cœurs) sont libérés.

 

2. Distributed MultiThreaded CheckPointing (DMTCP) : Usage

2.1. Principe de base et problématiques sur la plateforme CALCULCO

Au lieu de lancer classiquement un programme ./mon_programme.exe , il est lancé via une commande dmctp (dmtcp_launch ./mon_programme.exe) . Cette commande «2 en 1» lance  le programme au travers du coordinateur dmtcp . Ce dernier permet le contrôle de checkpoint via un jeu de socket TCP (port par défaut : 7779 ) et de signaux UNIX. Si un ordre de checkpoint est transmis au coordinateur, il produit un fichier checkpoint (ckpt-xxxxxxxxxxxx.dmcp) ainsi qu'un script dmtcp_restart_script.sh. Si le programme est arrêté (quelles qu'en soient les raisons), il suffira de relancer ce script pour que le programme reprenne à l'étape du checkpoint . 

Sur la plateforme, toutes les difficultés viennent du fait qu’un calcul est lancé par le gestionnaire de tâches OAR . Un jeu de signaux Unix entre OAR, le programme et le script de lancement OAR doit être mis en place pour que la magie opère.

  • comment lancer le (dmtcp,programme) via OAR?
  • comment capter (automatiquement) le signal de l'option OAR --signal ?,
  • comment, avec l’option OAR idempotent, relancer le script dmtcp_restart.sh
  • peut-on relancer les jobs sur un autre nœud?

 

Voici quelques exemples qui apportent des éléments de réponse. L'ensemble des test sont effectués avec de simples programmes «timer»  (de 5, 10  minutes)

 

2.1.1. exemple 1 : checkpoint utilisateur ( sans dmtcp )

  • le programme (timer de 4 mn) est trop long pour être exécuté dans le temps imparti (walltime :3mn30s)
  • OAR est lancé avec les options -t idempotent et --checkpoint
  • c’est le programme utilisateur qui prévoit de capter le signal checkpoint d’OAR pour effectuer lui-même la sauvegarde de son contexte:

extrait de la fonction sig_handler de compteur10mn.c:

void sig_handler(int signal)
{
switch (signal)
{
case SIGINT:
// code exécuté si on reçoit SIGINt (CTRL+C);
printf("SIGINT recu (programme C)\n");
printf("valeur du compteur enregistre: %d\n", compteur);
// fichier de sauvegarde du "contexte" ( l'indice = nb de secondes!
fp = fopen("context_compteur.txt", "wb");
if(fp == NULL) {
printf("error creating file");
}
else {
fwrite( &compteur , sizeof(int) , 1 , fp);
fclose(fp);
}
break;
...

 

fichiers sources:  

2.1.2. exemple 2 : checkpoint cyclique via dmtcp (sans contrôle des signaux) 

Même idée que précédemment mais le programme utilisateur ne «sait pas» gérer son contexte : DMTCP le fait pour lui.

  • le programme (timer de 10 mn) est trop long pour être exécuté dans le temps imparti (walltime :5mn)
  • OAR est lancé sans option particulière : il n’y a pas de redémarrage automatique
  • le programme est lancé via dmtcp dmtcp_launch -i [x seconde] ./PROG qui provoque un checkpoint toutes les x secondes ( doit évidemment être inférieur au walltime du job).
  • Le script checkpoint-dmtcp1.oar  doit être relancé à la main pour poursuivre l’exécution du programme.

Intérêt ?

Peut-être intéressant pour des programmes vraiment longs :

  • par sécurité (ex : coupure électrique)
  • besoin d’évaluer la pertinence de continuer l’exécution ( ou pas : -> oardel JOBID)
  • limiter en ressources la requête OAR ( nombre heures.cœurs )

fichiers sources:  

usage:

oarsub -S ./checpoint-dmtcp1.oar

extrait commenté du  fichier de sortie OAR.ckpt-dmtcp1.out

Debut du jobs ou restart ?
-> Debut du jobs
i = 0
i = 1
i = 2

i = 185
i = 186 # WALLTIME atteint: le job est tué.
# relance manuelle checkpoint-dmtcp1.oar 
Debut du jobs ou restart ?
-> reprise du job # relance (a la main ): il y a un ckpt, on ne repart pas de zéro
i = 170
i = 171

i = 239
i = 240
—post treatement—
fin effective du programme, suppression des checkpoints

 

2.1.3. exemple 3 : checkpoint via dmtcp avec contrôle des signaux

  • le programme (timer de 10 mn) est trop long pour être exécuté dans le temps imparti (walltime :6mn)
  • OAR est lancé avec les options -t idempotentet --checkpoint
  • le programme est lancé via dmtcp dmtcp_launch ./PROG (sans checkpoint)
  • Le checkpoint du programme est assuré par une gestion correct des signaux. Le script OAR capte le signal de checkpointing du SCHEDULER OAR et transmet un signal au coordinateur dmtcp afin qu’il déclenche un checkpoint du timer.
  • l’option idempotent d’OAR permet de relancer automatique le script jusqu’au terme du programme.

fichiers sources:  

 

3. Pour aller plus loin: recommandations, autres exemples 

 

Précautions à prendre, soucis à prévoir, dans tous les cas (ex 1 à 3)

1.  surveiller ses jobs (MONITORING). En cas de boucle infinie (oardel JOBID !)
2 . la taille des checkpoint est-elle raisonnable ?
3. «multiuser» sur le même noeud? Non décrit dans ce tutorial, le port standard du coordinateur DMTCP est 7779. Un deuxième utilisateur (et les suivants) ne pourra pas utiliser dmtcp sur le même nœud sans changer ce port par défaut. La solution est de lancer dmtcp avec l’option -p 0 ( tirage aléatoire d’un n° de port )… mais il faudra en plus gérer dans les scripts la récupération du numéro de port, les variables d’environnements du port et de l’hôte.
4. une reprise de job sur un autre nœud? le hostname est écrit en «dur» dans les scripts dmtcp_restart_script.sh: un simple sed permet de contourner le problème, s'inspirer de l'exemple checkpoint-dmtcp-ex6.oar (cf les tutoriaux sur GOGs ci-dessous)

 

 

DMTCP est capable d’effectuer des checkpoints de programmes plus complexes (multi-threads etc.), MATLAB n'est pas supporté sur la plateforme à ce jour (nov 2017). Des tests ont été effectués avec OpenMP, Cilk++.   D’autres exemples sont disponibles dans le dépot git  tutoriaux_calculco