Ejecucion de trabajos
Magerit se explota mediante trabajos batch usando SLURM como gestor y planificador de recursos. Para ejecutar se indican las características del trabajo que se necesita y el sistema se encargará de reservar los recursos necesarios y ejecutar las tareas.
Uso básico
El trabajo mínimo para ejecutar sería un fichero job.sh
con el contenido:
#!/bin/bash
module purge && module load <app>
srun <app> --app-param app_args
El trabajo se envía al sistema ejecutando sbatch job.sh
y ejecutaría 1 tarea (1 CPU) durante un máximo de 24 horas. Estos parámetros se pueden modificar mediante directivas de SLURM.
Aunque es posible especificar las directivas como argumentos de |
Slurm proporciona una serie de mandatos que permiten controlar los trabajos en ejecución:
Mandato | Descripción |
---|---|
Envía un trabajo al sistema devolviendo su identificador. | |
Lista los trabajos encolados en el sistema (aquellos que están ejecutando o esperando para ejecutar). | |
Cancela un trabajo encolado. Si el trabajo ya estaba ejecutando se abortará la ejecución en ese momento. | |
Muestra estadísticas de uso de un trabajo que esté ejecutando en ese momento. | |
Muestra información de la ejecución de un trabajo ya finalizado. |
Definición de trabajos
Las directivas de SLURM son comentarios que empiezan con #SBATCH
seguido de las mismas opciones que se indicarían a sbatch
.
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --partition=standard
#SBATCH --job-name=my_job
#SBATCH --ntasks=1
#SBATCH --mem-per-cpu=1G
#SBATCH --time=12:00:00
#SBATCH --mail-type=ALL
#SBATCH --mail-user=user@example.com
##------------------------ End job description ------------------------
module purge && module load <app>
srun <app> --app-param app_args
Es recomendable especificar únicamente las directivas mínimas que sean necesarias. |
Las directivas deben ser lo primero que aparezca en el fichero antes de cualquier línea que se pueda ejecutar. |
Aunque SLURM proporciona multitud de directivas para configurar un trabajo, existe un conjunto reducido de directivas comunes:
Opción | Abreviado | Descripción |
---|---|---|
|
| Nombre del trabajo. Es meramente informativo y sirve para facilitar la identificación. |
|
| Partición a utilizar. |
|
| Número de nodos a utilizar. |
|
| Número de tareas a ejecutar (paralelismo distribuido). |
| Número de tareas asignadas a cada nodo físico. | |
|
| Duración máxima de ejecución del trabajo. |
| Memoria RAM (MiB) requerida por CPU. Se recomienda indicar la unidad ( | |
| Memoria RAM (MiB) requerida por nodo. Se recomienda indicar la unidad ( Es preferible utilizar | |
|
| Fichero para almacenar la salida estandar del job. Se desaconseja su uso. |
|
| Fichero para almacenar la salida de errores del job. Se desaconseja su uso. |
| Dirección de correo para el envío de notificaciones de eventos | |
| Enviar correos electrónicos al usuario en todos los eventos de trabajo. | |
| Directorio de trabajo. |
Se puede obtener información detallada de todas las directivas disponibles consultando la documentación de sbatch o ejecutando man sbatch
en uno de los nodos interactivos.
Aunque se puede utilizar la versión abreviada, se recomienda utilizar la versión larga por claridad. Por ejemplo, es muy fácil confundir las opciones |
Límites de ejecución
Para facilitar una utilización eficiente de los recursos se han establecido una serie de límites. Estos límites permiten cierta flexibilidad para acomodar la ejecución de múltiples trabajos.
El principal límite es el trabajo máximo que se puede enviar al sistema. En el caso de la partición standard se pueden solicitar 600 cores o lo que es equivalente 2400 GiB de memoria RAM durante 72 horas. Sin embargo, es posible incrementar la duración de un trabajo si se solicitan menos cores (Ej.: se puede reservar 300 cores y 1200 GiB de RAM, que equivaldría a la mitad, hasta 144 horas, el doble de tiempo) aunque nunca se puede superar las 160 horas.
Partition | Recursos máximos (duración máxima) | Duración máxima |
---|---|---|
| 600 cores = 2400 GiB (72 horas) | 160 horas |
| 8 GPUs (72 horas) | 160 horas |
| 48 cores = 160 GiB (15 minutos) | 1 hora |
| 4 GPUs (5 minutos) | 20 minutos |
Los límites establecen una cota máxima siendo válida cualquier combinación que los cumpla.
En el caso de las particiones con GPU se aplican adicionalmente los límites de CPU y RAM de su partición equivalente.
Es muy importante hacer un uso correcto de los recursos, solicitando únicamente los necesarios (con cierto margen de seguridad) para la ejecución de cada trabajo. Esto facilita la planificación y hace que el trabajo pueda entrar antes a ejecutar. |
Redirección de salidas
Las aplicaciones suelen escribir datos o resultados por la consola. Al procesarme mediante un sistema de proceso por lotes, estas salidas se perderían al no existir una consola en el momento de ejecución. Para evitarlo SLURM redirige esas salidas a ficheros en disco dónde se pueden consultar.
Por defecto, SLURM almacena ambas salidas en el fichero slurm-<id_job>.out
. Este fichero se crea automáticamente al iniciar la ejecución en el directorio del trabajo.
Salvo que sea necesario realizar algún tratamiento sobre las salidas, está desaconsejado modificar el valor por omisión. |
Es posible redefinir el nombre y ubicación mediante las directivas --output=<outfile>
y --error=<errfile>
, pudiendo incluso separarlas en dos ficheros.
El directorio donde se almacenan las redirecciones debe existir y se debe poder crear los ficheros en su interior. Si no es así el trabajo abortará sin ningún tipo de indicación. |
Dentro del nombre existen algunos tokens que se reemplazarán automáticamente por los datos del trabajo:
Token | Descripción |
---|---|
| Evita el proceso de los reemplazos |
| Genera un único "%". |
| Identificador del trabajo (si se usan arrays) |
| Índice del trabajo (si se usan arrays). |
| Identificador de trabajo y paso (equivale a |
| Identificador del trabajo |
| Nombre del nodo. Creará un fichero diferente para cada nodo que participe en la ejecución. |
| Identificador del nodo. Creará un fichero diferente para cada nodo que participe en la ejecución. |
| Identificador del paso (número de srun ejecutados) |
| Identificador de la tarea (rank) dentro del trabajo. Creará un fichero diferente por cada tarea. |
| Usuario |
| Nombre del trabajo. |
Por ejemplo:
-
Redirigir la salida y el error a dos ficheros diferentes:
--output=out-%j.log --output=err-%j.log
-
Redirigir la salida y el error de cada paso y tarea a su propio fichero:
--output=out-%J.%t.log --output=err-%J.%t.log
Notificaciones
Se puede configurar el envío automático de un correo cuando se produzcan modificaciones en el estado de un trabajo.
Para ello hay que añadir las opciones --mail-user
indicando el correo al que se deben enviar las notificaciones y --mail-type
con los eventos de interés separados por comas:
-
BEGIN
-
END
-
FAIL
-
REQUEUE
-
STAGE_OUT
(el trabajo ha finalizado incluyendo las labores de limpieza.)
Se puede indicar ALL
como una abreviatura de todos los eventos anteriores y NONE
para ninguno
Adicionalmente se pueden solicitar avisos cuando se alcance cierto porcentaje el límite de tiempo especificado:
Evento | Porcentaje |
---|---|
| 100% |
| 90% |
| 80% |
| 50% |
Configuraciones específicas
Gracias a la flexibilidad en la configuración, Magerit permite la ejecución de trabajos con multitud de esquemas que dependen de la capacidad del software y de lo que se desee hacer.
En estos ejemplos sólo se indican las directivas mínimas de interés, aunque se pueden añadir y combinar con cualquier otra directiva. |
Trabajos paralelos con MPI
MPI (Message Passing Interface) es un mecanismo para la programación paralela por paso de mensajes que no necesita que todos los procesos ejecuten en el mismo nodo. La cantidad de estos procesos se maneja con la directiva --ntasks
.
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=<# tareas>
##------------------------ End job description ------------------------
module purge && module load <app>
srun <app> --app-param app_args
Trabajos secuenciales
Los trabajos secuenciales no utilizan paralelismo por lo que sólo tiene sentido solicitar una única tarea.
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=1
##------------------------ End job description ------------------------
module purge && module load <app>
srun <app> --app-param app_args
En este esquema de ejecución el número de tareas |
Este tipo de trabajos suelen soportar OpenMP ya sea directamente en la aplicación o mediante el uso de alguna librería que lo haga. En estos casos, la ejecución puede mejorar si se solicitan más recursos.
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=<# cpus>
##------------------------ End job description ------------------------
module purge && module load <app>
export OMP_NUM_THREADS="${SLURM_CPUS_PER_TASK}"
srun <app> --app-param app_args
Trabajos híbridos
Los trabajos híbridos MPI + OpenMP consisten en ejecutar varios procesos MPI (definidos por --ntasks
) y, cada uno de ellos, desplegará varios hilos OpenMP (definidos por --cpus-per-task
). A efectos contables, equivale a reservar ntasks × cpus-per-task CPUs.
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=<# tasks>
#SBATCH --cpus_per_task=<# cpus>
##------------------------ End job description ------------------------
module purge && module load <app>
export OMP_NUM_THREADS="${SLURM_CPUS_PER_TASK}"
srun <app> --app-param app_args
Es necesario que la aplicación implemente estos dos paralelismos para obtener el rendimiento deseado. |
Trabajos usando aceleradores (CUDA/GPU)
Los trabajos pueden solicitar el uso de aceleradores CUDA en los nodos que disponen de ellos.
Es necesario que la aplicación esté adaptada para hacer uso de CUDA o no se observará ningún tipo de mejora. Las aplicaciones compiladas expresamente con soporte de aceleradores contendrán |
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=<# tasks>
#SBATCH --partition=standard-gpu
#SBATCH --gres=gpu:<type>:<# gpus>
##------------------------ End job description ------------------------
module purge && module load <app>
srun <app> --app-param app_args
El tipo puede ser a100
o v100
.
Tipo | Descripción |
---|---|
| Una tarjeta NVIDIA A100. Se dispone de 4 nodos con 4 NVIDIA A100 cada uno de ellos. |
| Una tarjeta NVIDIA V100. Se dispone de 2 nodos con 2 NVIDIA V100 cada uno de ellos. |
También es posible solicitar cualquier tipo si no existe ninguna preferencia, omitiendo esa parte en la directiva (--gres=gpu:<# gpus>
) Se asignará la primera que esté disponible pudiendo incluso mezclarlas.
Si no se indica el tipo expresamente, el sistema puede asignar cualquiera de los modelos, incluso mezclados si se solicita más de una tarea. Dado que las prestaciones son diferentes, se recomienda no utilizar esta opción. Puede ser útil si se necesita un único acelerador y no se quiere esperar por la disponibilidad de un modelo concreto. |
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=<# tasks>
#SBATCH --partition=standard-gpu
#SBATCH --gres=gpu:<# gpus>
##------------------------ End job description ------------------------
module purge && module load <app>
srun <app> --app-param app_args
Como ya se indicó, estas directivas se pueden combinar con cualquier otra para generar configuraciones. Una de las configuraciones más habituales es incrementar el número de procesadores asignados a una tarea (directiva |
Múltiples programas
Aunque lo más común es ejecutar el mismo programa en todas las tareas, en ocasiones es necesario ejecutar diferentes operaciones (MPMD — Multiple Program Multiple Data). Un posible caso es cuando se quiere aglutinar en una simple ejecución diferentes procesos de corta duración para ejecutarlos en un único job.
Para simplificar estas ejecuciones existe la configuración multi-programa de SLURM, que se activa con la opción --multi-prog
. Para ello hay que definir dos ficheros:
#!/bin/bash
##----------------------- Start job description -----------------------
#SBATCH --ntasks=<# tasks>
##------------------------ End job description ------------------------
module purge && module load <app>
export OMP_NUM_THREADS="${SLURM_CPUS_PER_TASK}"
srun --multi-prog job.list
La línea de srun simplemente indica el fichero donde se encuentran las ejecuciones que se desean hacer. Este fichero tiene dos campos:
Campo | Descripción |
---|---|
| Identificador de la tarea a ejecutar (número de rank de MPI). Empieza en cero. Es posible indicar conjuntos (0,2,5), rangos (0-3) o cualquier combinación (0,2-3,5). |
| Programa que se desea ejecutar seguido de los argumentos que precise. Se puede utilizar la expresión |
4-6 cmd1
1,7 cmd2 task:%t
0,2-3 cmd3 offset:%o
El número de tareas ( |
Distintas compilaciones
En ocasiones Magerit tiene varias compilaciones de aplicaciones. Una de estas versiones estará directamente cargada y será la que utilicen todos los trabajos si no se indica lo contrario.
Los trabajos pueden elegir cuál de ellas utilizar mediante el uso de un module llamado apps
. Para elegir una compilación diferente, basta con cargar la versión del module apps
adecuada.
#!/bin/bash
##----------------------- Start job description -----------------------
# [...]
##------------------------ End job description ------------------------
module purge && module load apps/<year> && module load <app>
srun <app> --app-param app_args
Para ver qué modules están disponibles es posible cargarlo el module apps
en una sesión interactiva y listar los modules disponibles.