Running parallel jobs based on Rscript

,

Bonjour,

J'ai un script R qui fait une évaluation comparative de méthodes l'apprentissage (RF, KNN, SVM) sur des jeux de données RNA-seq, en parallélisant les tests avec la librairie R doParallel. Les analyses prennent pas mal d'heures.

Précédemment, j'ai utilisé R sous sinteractive pour lancer les tâches une par une, et je voudrais maintenant tout lancer sur srun.

Je lance l'exécution avec la commande suivante.

srun --mem=32GB \
>   --output ${OUT_FILE} \
>   --error ${ERR_FILE} \
>   Rscript --vanilla misc/main_processes.R

Ca tourne, mais contrairement à ce que je croyais, srun reste bloqué, et je ne sais donc pas ce qui se passera si je ferme ma session ssh, ou si je me fais éjecter.

Quelle serait la façon correcte d'envoyer un job à slurm pour pouvoir ensuite quitter la session ssh et revenir voir toutes les quelques heures où en sont les fichiers de log (error, output) ?

Merci

Jacques

Bon, je me suis cassé la pipe

packet_write_wait: Connection to 192.54.201.181 port 22: Broken pipe

image

mais apparemment ça n'empêche pas srun de bravement continuer son boulot.

Je pense qu'il serait préférable d'utiliser sbatch pour ce besoin.
sbatch va te permettre de soumettre l'execution d'un script au cluster et ne nécessite pas que tu maintiennes ta connexion SSH active.

Voici comment procéder :
Tout d'abord écrit un petit script pour lancer ton traitement (par exemple my_processes.sh) :

#!/bin/bash
module load r
srun misc/main_processes.R

srun te permet de marquer (et paramétrer) une étape de ton job (un job step)

A présent il te suffit de lancer ce script avec sbatch :

$ sbatch my_processes.sh

En retour tu obtiendras un numéro de job.
Slurm va automatiquement créer un fichier de sortie pour ton job de la forme slurm_xxxxx.out ou xxxxx est l'identifiant de ton job dans ton répertoire courant.

Tu peux ensuite surveiller l'activité de ton job à l'aide des commandes squeue, sacct ou sstat.

Merci Julien,

En pratique c'est un tout petit peu plus compliqué car il faut gérer

  • le chargement du module conda,
  • l'environnement conda spécifique dont j'ai besoin (avec R3.6.1 + des librairies),
  • la mémoire,
  • peut-être d'autres paramètres.

J'ai fait un script bash ci-dessous qui charge l'environnement, et lance 2 tâches avec srun (la même analyse avec 2 jeux de données différents).

#!/bin/bash


## Note: the loading of conda module and environment must apparently be done
## in the sbatch that calls this script, and not within the script itself
# module load conda
# conda init bash
# conda activate rnaseqmva
cd /shared/projects/rnaseqmva/RNAseqMVA
WORKSPACE=/shared/projects/rnaseqmva/RNAseqMVA_workspace
LOG_DIR=${WORKSPACE}/logs
mkdir -p ${LOG_DIR}

cd /shared/projects/rnaseqmva/RNAseqMVA
START_DATE=`date +%Y-%m-%d_%H%M%S`

## !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
## First analysis

## Parameters
RECOUNT_ID=SRP056295
FEATURE=transcript
PREFIX=${RECOUNT_ID}_${FEATURE}_${START_DATE}

## Submit the job to slurm job scheduler via srun
srun --mem=32GB \
  --output ${LOG_DIR}/${PREFIX}_out.txt \
  --error ${LOG_DIR}/${PREFIX}_err.txt \
  Rscript --vanilla misc/main_processes.R ${RECOUNT_ID} ${FEATURE}

## !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
## Second analysis

## Parameters
RECOUNT_ID=SRP066834
FEATURE=gene
PREFIX=${RECOUNT_ID}_${FEATURE}_${START_DATE}

## Submit the job to slurm job scheduler via srun
srun --mem=32GB \
  --output ${LOG_DIR}/${PREFIX}_out.txt \
  --error ${LOG_DIR}/${PREFIX}_err.txt \
  Rscript --vanilla misc/main_processes.R ${RECOUNT_ID} ${FEATURE}

Ensuite je le lance avec sbatch.

## Initiate the environment
module load conda
conda init bash
conda activate rnaseqmva

cd /shared/projects/rnaseqmva/RNAseqMVA

## Send the script to the job scheduler
sbatch --mem=32GB srun_analysis.sh

Ca tourne, mais apparemment je n'ai qu'un seul job en queue.

Je me demande donc comment on fait pour lancer plusieurs analyses indépendamment, en les distribuant sur différents noeuds si nécessaire.

Merci

Jacques

J'ai lancé la deuxième analyse séparément (en relançant sbatch sur le script bash mais avec le second cas d'étude). J'ai maintenant 2 jobs qui tournent, mais sur le même noeud (82).

(base) [jvanhelden@cpu-node-82 ~]$ squeue -u jvanhelden
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
           3147815      fast srun_ana jvanheld  R      40:25      1 cpu-node-82
           3147822      fast srun_ana jvanheld  R      10:54      1 cpu-node-82

Je devrais lancer une douzaine de cas d'étude, et chacun mobilise 26 coeurs (je parallélise avec la librairie R doParallelize). J'imagine que ce ne sera pas formidable si sbatch envoie tout sur le même noeud. Est-ce que je devrais utiliser des options quand je lance srun pour indiquer qu'il faut réserver 26 coeurs ?

Bref: comment faire pour lancer 12 jobs qui chacun mobilisent 26 coeurs ?

Merci

Jacques

Je ne suis pas sûr d'avoir bien saisi si tu souhaites lancer les deux analyses en parallèle ou si les analyses sont séquentiels.
Les deux sont possibles bien entendu, mais il faudra prévoir le double de RAM si les analyses doivent être lancé en parallèle.

Quelques remarques :

  • sbatch permet de créer un job alors que srun va te permettre de définir un job step (une étape de ton job) en utilisant tout ou parti des ressources réservées pour le job.
  • par défaut srun utilise l'ensemble des ressources disponibles pour le job, ainsi si tu as besoin de toute la RAM réservée pour le job, il n'est pas nécessaire de préciser une quantité de RAM à srun
  • Les srun n'étant pas des jobs, tu n'obtiendras pas de numéro de jobs pour les 2 étapes de ton scripts mais uniquement un numéro d'identifiant pour l'ensemble du job. Tu peux suivre ensuite via sacct l'avancé de chaque job step (voir ci-dessous).
  • Il n'est pas nécessaire de créer 2 scripts. En effet, dans tout job SLURM, il existe un job step "batch" qui te permet de faire des initialisations ou des paramétrages. Il suffit de ne pas utiliser la commande srun devant tes commandes d'initialisation pour qu'elle soit associé au job step batch.

Voici un script unique qui réalise tes deux traitements de manière séquentielle :

#!/bin/bash

#SBATCH --mem=32GB  # request 32GB of RAM for the whole job

## Initiate the environment
module load conda
conda init bash
conda activate rnaseqmva

## Parameters
RECOUNT_ID=SRP056295
FEATURE=transcript
PREFIX=${RECOUNT_ID}_${FEATURE}_${START_DATE}

## First job step
srun --mem=32GB \
  --output ${LOG_DIR}/${PREFIX}_out.txt \
  --error ${LOG_DIR}/${PREFIX}_err.txt \
  Rscript --vanilla misc/main_processes.R ${RECOUNT_ID} ${FEATURE}

## !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
## Second analysis

## Parameters
RECOUNT_ID=SRP066834
FEATURE=gene
PREFIX=${RECOUNT_ID}_${FEATURE}_${START_DATE}

## Second job step
srun --mem=32GB \
  --output ${LOG_DIR}/${PREFIX}_out.txt \
  --error ${LOG_DIR}/${PREFIX}_err.txt \
  Rscript --vanilla misc/main_processes.R ${RECOUNT_ID} ${FEATURE}

Tu peux lancer ce script directement avec sbatch :

$ sbatch my_analysis.sh

sbatch va alors t'indiquer le numéro de job créé pour cette analyse (par exemple 12345678).

Tu peux alors suivre le déroulement du job et de ses étapes à l'aide de sacct :

$ sacct -o JobID%15,JobName%40,Start,End,CPUTime,State -j 12345678

L'option -o permet de préciser le format de sortie souhaité

Indique moi si tu souhaites réaliser les deux traitements en parallèle et je te montrerai comment adapter ton script.

Effectivement je désire lancer les jobs en parallèle. Ou plus précisément, je désire placer mes 12 jobs dans une queue, et qu'ils soient exécutés quand c'est possible en fonction de la disponibilité des noeuds, sachant sont complètement indépendants les uns des autres. En gros je cherche la simple fonctionnalité de qsub dans PBS ou SGE : soumettre une série de jobs indépendants à un gestionnaire de jobs, puis pouvoir suivre leur exécution.

Chacun des jobs demande pas mal de mémoire car il y fait tourner 50 répétitions d'un test sur un algo de classification supervisée (SVM, Random Forest ou KNN). Pour ne pas y passer des semaines, je parallélise les 50 répétitions avec la librairie R doParallel qui permet d'exécuter une boucle foreach en parallèle. Je ne sais pas comment il fait en pratique, j'ai l'impression que ça s'apparente à des files (threads) mais je ne suis pas sûr.

Bref, au moment d'envoyer mes jobs en queue, je voudrais m'assurer qu'ils aient suffisamment de mémoire, mais aussi qu'on leur réserve 50 coeurs (j'avais limité à 26 car les machines en ont apparemment 32).

Tu vois l'idée ?

Je vois l'idée :wink:

L'équivalent de qsub est tout simplement sbatch.

Tu peux au choix écrire un petit script qui va lancer 12 sbatch ou alors utiliser le mécanisme des jobs array de SLURM.

Les jobs arrays permettent de lancer un même script n fois en faisant varier la valeur d'un index. On peut alors utiliser l'index pour choisir les fichiers à passer en entrée de l'analyse ou pour faire varier les paramètres.

Je te donne ci-dessous un exemple de script utilisant le mécanisme des jobs arrays à adapter à tes besoins :

#!/bin/bash

#SBATCH --array=1-12  # nous demandons ici à SLURM de créer 12 jobs (notre script sera lancé 12 fois)
#SBATCH --cpus=50  # nous demandons 50 cpus
#SBATCH --mems=32GB # nous réservons 32Go par job
#SBATCH -o analysis-%j-%a_out.txt  # fichier de sortie standard du job
#SBATCH -e analysis-%j-%a_err.txt  # fichier de sortie d'erreur du job

## Initiate the environment
module load conda
conda init bash
conda activate rnaseqmva

## La variable $SLURM_ARRAY_TASK_ID prendra une valeur différente (1 à 12) pour chaque job
srun Rscript --vanilla misc/main_processes.R $SLURM_ARRAY_TASK_ID

On lance le script avec sbatch qui se chargera de lancer les 12 jobs :

$ sbatch my_analysis.sh

La plupart des noeuds dispose de 52 CPUs (nous avons activé l'hyperthreading récemment), ainsi les jobs devrait occuper 12 noeuds. Cependant, la limite actuelle est de 400 CPUs par utilisateur à un instant donné, ainsi tu verras au maximum 7 jobs (364 cpus) en parallèle.

Mais comment fais-je pour fournir les paramètres qui correspondent à chaque valeur de SLURM_ARRAY_TASK_ID ? Avec ton exemple j'obtiens des valeurs de 1 à 12, alors que je voudrais pouvoir combiner deux paramètres

RECOUNT_IDS=SRP042620 SRP056295 SRP057196 SRP061240 SRP062966 SRP066834
FEATURES=gene transcript

Ce sont ces deux paramèters-là que je dois fournir comme arguments à mon Rscript

srun Rscript --vanilla misc/main_processes.R SRP042620 gene
srun Rscript --vanilla misc/main_processes.R SRP042620 transcript
srun Rscript --vanilla misc/main_processes.R SRP056295 gene
srun Rscript --vanilla misc/main_processes.R SRP056295 transcript
...

Y a-t-il moyen d'associer les arrays à des variations sur des paramètres de type chaîne de caractère ?

Tiens, l'université de Michigan a un tuto sur l'utilisation de R avec Slurm sur leur cluster. Ils ont développé un package interne pour faciliter la gestion des jobarrays à partir de R.

https://sph.umich.edu/biostat/computing/cluster/examples/r.html

Ca ne répond toujours pas à ma question concernant la façon de gérer une combinaison de paramètres avec des valeurs numériques, mais ça me ramène à ma proposition de faire un tuto spécifique pour R avec Slurm sur le cluster IFB-core (échanges avec @dbenaben), avec un ou deux exemples réalistes complets (sur le site umich ils fournissent juste des morceaux de script, on doit ensuite imaginer comment combiner tout cela) qui éviterait à d'autres usagers de se casser la tête.

Mais avant cela il faut trouver la façon la plus correcte de procéder.

Yes !

Voici un exemple de script :

#!/bin/bash

#SBATCH --array=0-11  # on démarre l'array à 0
#SBATCH --cpus=50
#SBATCH --mems=32GB
#SBATCH -o analysis-%j-%a_out.txt
#SBATCH -e analysis-%j-%a_err.txt

## Initiate the environment
module load conda
conda init bash
conda activate rnaseqmva

# liste des paramètres
RECOUNT_IDS=(SRP042620 SRP056295 SRP057196 SRP061240 SRP062966 SRP066834)
FEATURES=(gene transcript)

# calcul le recount et la feature en fonction de l'index courant du job
feature=$((SLURM_ARRAY_TASK_ID % 2))
recount_id=$((SLURM_ARRAY_TASK_ID / 2))

# lancement de l'analyse
srun Rscript --vanilla misc/main_processes.R ${RECOUNT_IDS[recount_id]} ${FEATURES[feature]}

Ca tourne !

squeue -u $UID
             JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
   3148928_[10-13]      fast srun_job jvanheld PD       0:00      1 (QOSMaxCpuPerUserLimit)
         3148928_0      fast srun_job jvanheld  R       3:21      1 cpu-node-10
         3148928_2      fast srun_job jvanheld  R       3:21      1 cpu-node-7
         3148928_4      fast srun_job jvanheld  R       3:21      1 cpu-node-9
         3148928_5      fast srun_job jvanheld  R       3:21      1 cpu-node-11
         3148928_6      fast srun_job jvanheld  R       3:21      1 cpu-node-12
         3148928_7      fast srun_job jvanheld  R       3:21      1 cpu-node-13
         3148928_9      fast srun_job jvanheld  R       0:49      1 cpu-node-6
         3148928_8      fast srun_job jvanheld  R       0:52      1 cpu-node-8

Par contre j'ai un de mes jobs précédents qui s'est arrêté au bout de 24h car il avait dépassé le temps imparti. Est-ce que je peux lancer cela sur la queue "long" ? Comment ?

Y a-t-il une pénalité à utiliser cette queue (moins de noeuds/cpu en même temps) ?

Super !

Pour les jobs plus long (jusqu'à 30 jours), tu peux utiliser la partition "long" avec l'option -p long.

Il n'y a pas de pénalité particulière en utilisant cette partition, mais elles ne disposent que de 21 noeuds.