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 -l
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 -l
##----------------------- 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 -l
##----------------------- 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 -l
##----------------------- 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 -l
##----------------------- 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 -l
##----------------------- 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 -l
##----------------------- 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 , v100 o l40s .
| 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. |
| Una tarjeta NVIDIA L40S. Se dispone de 5 nodos con 4 NVIDIA L40S cada uno de ellos. |
| La NVIDIA L40S está diseñada para acelerar cargas de trabajo de inteligencia artificial y procesamiento gráfico en centros de datos, especialmente en tareas como IA generativa, entrenamiento e inferencia de modelos de lenguaje, renderizado y gráficos 3D. Por su diseño, la L40S resulta adecuada para flujos de trabajo que se beneficien de precisión simple o mixta. Su arquitectura Ada Lovelace está optimizada para el rendimiento en coma flotante de precisión simple (FP32) y aprovecha Tensor Cores, que permiten operar con precisiones reducidas o mixtas (FP16, FP8), aumentando la eficiencia en cargas de trabajo de aprendizaje automático. En cambio, |
| Asignación de GPU en los trabajos: Es muy importante especificar el tipo de GPU o el requerimiento de precisión FP64 al enviar un trabajo para garantizar que se ejecute en el hardware adecuado. Esto se puede hacer mediante las directivas Usando una de las siguientes Usando una de las siguientes Si no se indica el tipo de GPU de forma explícita, el sistema puede asignar cualquier modelo disponible, incluso combinarlos si se solicitan varias GPUs. Esta opción solo puede ser útil cuando se necesita un único acelerador y no se desea esperar a la disponibilidad de un modelo concreto; dado que las prestaciones varían entre modelos, no se recomienda en ningún otro caso. |
#!/bin/bash -l
##----------------------- 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 -l
##----------------------- 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 -l
##----------------------- 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.