Antes de empezar
En esta instancia se asume que ya se dispone de una cuenta en Quipu, y que se sabe interactuar vía línea de comando con una terminal Linux.
Un quipu registraba información anudando cuerdas colgantes de un cordón principal. Este clúster hace algo parecido: quipu es el nodo de acceso, y de él "cuelgan" siete nodos de cómputo — geofisica1 a geofisica7 —, cada uno con 8 procesadores. Esta guía explica cómo compilar sus programas y cómo enviarlos a ejecutar con Slurm.
Módulos
Los programas de cómputo disponibles en Quipu se manipulan mediante la instrucción module.
Módulos cargados
module list
Currently Loaded Modules:
1) autotools 3) gnu12/12.4.0 5) ucx/1.15.0 7) openmpi4/4.1.6
2) prun/2.2 4) hwloc/2.7.2 6) libfabric/1.19.0 8) ohpc
El listado muestra las librerías que se cargan automáticamente al loguearse. Permiten compilar y ejecutar código Fortran y C en serie y paralelo en el cluster. Sin embargo, no son las únicas disponibles.
Módulos disponibles
module avail
------------------ /opt/ohpc/pub/moduledeps/gnu12-openmpi4 -------------------
adios/1.13.1 netcdf-cxx/4.3.1 py3-scipy/1.5.4
boost/1.81.0 netcdf-fortran/4.6.0 scalapack/2.2.0
dimemas/5.4.2 netcdf/4.9.0 scalasca/2.5
extrae/3.8.3 omb/6.1 scorep/7.1
fftw/3.3.10 opencoarrays/2.10.1 sionlib/1.7.7
hypre/2.18.1 petsc/3.18.1 slepc/3.18.0
imb/2021.3 phdf5/1.10.8 superlu_dist/6.4.0
libmesh/1.8.0 pnetcdf/1.12.3 tau/2.31.1
mfem/4.4 ptscotch/7.0.1 trilinos/13.4.0
mumps/5.2.1 py3-mpi4py/3.1.3
----------------------- /opt/ohpc/pub/moduledeps/gnu12 ------------------------
R/4.2.1 mvapich2/2.3.7
gsl/2.7.1 openblas/0.3.21
hdf5/1.10.8 openmpi4/4.1.6 (L)
impi/2021.2.0.rpmsave pdtoolkit/3.25.1
impi/2021.10.0.rpmnew.rpmsave plasma/21.8.29
impi/2021.10.0 (D) py3-numpy/1.19.5
likwid/5.2.2 scotch/6.0.6
metis/5.1.0 superlu/5.2.1
mpich/3.4.3-ofi
-------------------------- /opt/ohpc/pub/modulefiles --------------------------
EasyBuild/4.9.4 libfabric/1.19.0 (L)
autotools (L) ohpc (L)
charliecloud/0.15 os
cmake/3.24.2 papi/6.0.0
gnu12/12.4.0 (L) pmix/4.2.9
hwloc/2.7.2 (L) prun/2.2 (L)
intel/2021.2.0.rpmsave spack/0.22.2
intel/2023.2.1.rpmnew.rpmsave ucx/1.15.0 (L)
intel/2023.2.1 (D) valgrind/3.19.0
La (L) muestra las librerías ya cargadas, la (D) la librería que se carga por defecto, cuando hay más de una versión de la misma.
Cargar un módulo
Para cargar una librería, se utiliza la instrucción module load seguida del nombre de la librería. Por ejemplo:
module load R
carga la librería R/4.2.1.
Otras acciones
Otras posibles acciones de module son:
module purge Inhabilita todas las librerías.
module swap m1 m2 Inhabilita la librería m1 y habilita m2
module unload m1 Inhabilita la librería m1
Ayuda
Para obtener más información acerca de module:
module --help
Cómo compilar programas
Compilar programas: serie y paralelo
El clúster ofrece dos familias de compiladores mediante module: el conjunto GNU (gnu12/12.4.0) y el conjunto Intel (intel/2023.2.1, por defecto). Para programas paralelos con MPI, en ambos casos se usa el módulo openmpi4: existen dos compilaciones distintas detrás del mismo nombre, una enlazada con GNU y otra con el toolchain Intel, y Lmod activa automáticamente la que corresponde según qué compilador se haya cargado antes. También está disponible impi (Intel MPI) como implementación alternativa bajo el árbol Intel, para quien la prefiera.
GNU — programas en serie
Se carga el compilador y se compila normalmente con gcc, g++ o gfortran.
# cargar el compilador GNU
module load gnu12
# C
gcc -O2 -o programa programa.c
# C++
g++ -O2 -o programa programa.cpp
# Fortran
gfortran -O2 -o programa programa.f90
GNU — programas paralelos (MPI y OpenMP)
Para MPI, se carga además openmpi4, que habilita los compiladores envolventes mpicc, mpic++ y mpif90. Para OpenMP basta con la bandera -fopenmp.
# cargar GNU + OpenMPI
module load gnu12 openmpi4
# MPI en C
mpicc -O2 -o programa_mpi programa_mpi.c
# MPI en Fortran
mpif90 -O2 -o programa_mpi programa_mpi.f90
# OpenMP (memoria compartida, un solo nodo)
gcc -O2 -fopenmp -o programa_omp programa_omp.c
Sobre los módulos científicos: al cargar gnu12 y openmpi4 queda disponible la jerarquía gnu12-openmpi4, con bibliotecas ya compiladas para esa combinación: petsc, hypre, fftw, scalapack, mumps, py3-mpi4py, entre otras. Estas se cargan después de openmpi4, por ejemplo: module load gnu12 openmpi4 fftw petsc.
Intel — programas en serie
El módulo intel/2023.2.1 es el predeterminado y trae los compiladores modernos icx (C), icpx (C++) e ifx (Fortran).
# cargar el compilador Intel (toma el predeterminado 2023.2.1)
module load intel
# C
icx -O2 -o programa programa.c
# C++
icpx -O2 -o programa programa.cpp
# Fortran
ifx -O2 -o programa programa.f90
Intel — programas paralelos (MPI y OpenMP)
Al cargar intel y luegoopenmpi4 se activa una compilación de OpenMPI enlazada específicamente con el toolchain Intel (openmpi4-intel), distinta de la que se usa con GNU. Los envoltorios mpicc, mpic++ y mpif90 invocan los compiladores modernos de Intel (icx, icpx, ifx). OpenMP se activa con -qopenmp.
# cargar Intel + OpenMPI (compilación enlazada con Intel)
module load intel openmpi4
# MPI en C
mpicc -O2 -o programa_mpi programa_mpi.c
# MPI en Fortran
mpif90 -O2 -o programa_mpi programa_mpi.f90
# OpenMP
icx -O2 -qopenmp -o programa_omp programa_omp.c
Cargar el compilador antes que la librería MPI. El orden importa: module load intel openmpi4 activa la versión de openmpi4 enlazada con Intel; module load gnu12 openmpi4 activa la enlazada con GNU. Para confirmar cuál está activa y qué compilador usarán los envoltorios, se ejecuta mpif90 -show: debe mostrar ifx (no ifort) cuando se cargó junto con Intel. Cargando intel y openmpi4 también se habilita la jerarquía intel-openmpi4, análoga a gnu12-openmpi4, con las mismas bibliotecas científicas compiladas para ese toolchain.
Revisar versiones. Hay varias versiones de intel instaladas (algunas marcadas .rpmsave/.rpmnew, restos de actualizaciones). Se utiliza module avail intel para ver todas, y se carga una versión explícita si el código lo requiere, por ejemplo module load intel/2023.2.1.
Cómo correr programas
Si bien la compilación de los programas se realiza en el headnode, NUNCA deben ejecutarse en el mismo, ya que no tiene capacidad para atender los requerimientos de todos los usuarios.
El cluster quipu cuenta con 7 nodos de cómputo (geofisica1 a
geofisica7), cada uno con 8 núcleos físicos (16 hilos con
hyperthreading) y 32GB de RAM — 56 núcleos / 224GB en total. La gestión de
trabajos está a cargo de Slurm, y los trabajos se envían a una de tres
particiones (colas), cada una pensada para un tipo de carga de trabajo
distinto.
| Partición | Quién puede usarla | Tiempo máx. | Nodos máx./trabajo | Memoria por defecto | ¿Por defecto? |
|---------------|-------------------------|--------------|----------------------|----------------------|----------------|
| amplia | Todos | 3 días | 4 nodos | 3.8 GB/núcleo | SI |
| corta | Todos | 2 horas | 1 nodo | 3.8 GB/núcleo | No |
| larga | Todos | 14 días | 1 nodo | 3.8 GB/núcleo | No |
Si no se especifica una partición al enviar un trabajo, este va automáticamente a amplia.
Para mayor información sobre las colas, ver la sección Recursos (colas) Disponibles
Comandos esenciales de SLURM
sinfo: Estado de los nodos y particiones disponibles
sbatch script.sh: Enviar un trabajo a ejecución
squeue -u $USER: Ver mis trabajos en cola o en ejecución
scancel : Cancelar un trabajo
salloc ...: Reservar recursos para una sesión interactiva
sacct -j [jobid]: Ver estado/código de salida de un trabajo finalizado (limitado sin base de contabilidad)
Los nombres de partición y los límites de tiempo/memoria dependen de cómo esté configurado slurm.conf en este clúster. Se ejecuta sinfo para ver el nombre real de la(s) partición(es), y se reemplaza --partition=compute en los ejemplos por el nombre correspondiente.
Estructura de un script de trabajo (sbatch)
Todos los scripts que sean enviados con sbatch deberían incluir, como mínimo, las siguientes líneas al principio:
#!/bin/bash
#SBATCH --job-name=mi_trabajo # nombre identificatorio del trabajo
#SBATCH --partition=amplia # cola a utilizar
#SBATCH --time=1-00:00:00 # tiempo máximo solicitado
#SBATCH --output=%x_%j.out # archivo de salida estándar (stdout)
#SBATCH --error=%x_%j.err # archivo de salida de error (stderr)
%x se reemplaza automáticamente por el --job-name y %j por el
número de trabajo (jobid) que asigna Slurm — así cada corrida genera sus
propios archivos sin pisar los de corridas anteriores (por ejemplo,
mi_trabajo_1042.out y mi_trabajo_1042.err). Si se prefiere juntar ambas
salidas en un solo archivo, alcanza con no definir --error (todo se
escribe en --output), aunque en general conviene mantenerlos separados
para poder revisar los errores rápidamente sin filtrar la salida normal.
⚠️ Módulos: hay que cargarlos también dentro del script
Si tu programa fue compilado usando module load (por
ejemplo, un compilador, una librería MPI, una versión particular de
Python, etc.), ese mismo module load tiene que estar también dentro
del script que sea enviado con sbatch. Los módulos que se encuentran cargados en la
sesión de terminal del usuario (por ejemplo los que fueron cargados a mano antes de
compilar) no se heredan automáticamente en el entorno del trabajo que
corre en los nodos de cómputo — cada trabajo arranca con un entorno
limpio.
Si este paso es olvidado, el trabajo puede fallar con errores del tipo
command not found, error while loading shared libraries, o
comportarse de forma distinta a como fue probado interactivamente. Como
regla general: los módulos que fueron usados para compilar son, como mínimo,
los que deben cargarse para ejecutar.
module purge
module load gcc/12.2.0
module load openmpi/4.1.5
(Los nombres deben ajustarse a los módulos reales que fueron usados — es posible ver los
disponibles con module avail y los que se encuentran cargados actualmente con
module list.)
Ejemplos
Ejemplo 1 — trabajo en serie (un núcleo)
Para un programa que corre en un solo núcleo, sin paralelismo:
#!/bin/bash
#SBATCH --job-name=serie_ejemplo
#SBATCH --partition=amplia
#SBATCH --time=02:00:00
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=1
#SBATCH --mem=4G
#SBATCH --output=%x_%j.out
#SBATCH --error=%x_%j.err
module purge
module load gcc/12.2.0
./mi_programa_serie < entrada.dat
Si este script se guarda con el nombre serie.sh, para ser enviado a correr se ejecuta en la carpeta donde está, en este caso mimodelo1:
usuario@quipu:~/mimodelo1$ sbatch serie.sh
Ejemplo 2 — trabajo en paralelo (OpenMP, un solo nodo)
Para un programa compilado con soporte de memoria compartida (OpenMP), donde todos los hilos corren dentro de un mismo nodo:
#!/bin/bash
#SBATCH --job-name=openmp_ejemplo
#SBATCH --partition=amplia
#SBATCH --time=04:00:00
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=8
#SBATCH --mem=16G
#SBATCH --output=%x_%j.out
#SBATCH --error=%x_%j.err
module purge
module load gcc/12.2.0
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
./mi_programa_openmp
--cpus-per-task le dice a Slurm cuántos núcleos reservar para los
hilos; OMP_NUM_THREADS le dice al programa cuántos hilos usar en
tiempo de ejecución — conviene que coincidan, y usar la variable
$SLURM_CPUS_PER_TASK evita tener que actualizar el número en dos
lugares si después se cambia la cantidad de núcleos solicitados.
Para la ejecución del script, se procede de forma idéntica a la indicada en el Ejemplo 1.
Ejemplo 3 — trabajo en paralelo (MPI, múltiples nodos)
Para un programa compilado con MPI, que puede distribuirse en más de un nodo:
#!/bin/bash
#SBATCH --job-name=mpi_ejemplo
#SBATCH --partition=amplia
#SBATCH --time=1-00:00:00
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=8
#SBATCH --mem-per-cpu=3800
#SBATCH --output=%x_%j.out
#SBATCH --error=%x_%j.err
module purge
module load gcc/12.2.0
module load openmpi/4.1.5
srun ./mi_programa_mpi
Con srun alcanza para lanzar el ejecutable MPI dentro de un script de
Slurm (no hace falta mpirun/mpiexec por separado); Slurm se encarga
de repartir las tareas entre los nodos y núcleos solicitados según
--nodes y --ntasks-per-node.
Ejemplo 4 — trabajo en serie y paralelo con Pyhton
El clúster ofrece Python 3 con paquetes científicos precompilados a través de module, bajo el árbol GNU: py3-numpy/1.19.5, py3-scipy/1.5.4 y, dentro de la jerarquía MPI, py3-mpi4py/3.1.3. El intérprete detrás de estos módulos es Python 3.6 (el Python de sistema sobre el que se compiló este conjunto de paquetes).
Uso básico: numpy y scipy
Se carga gnu12 y el módulo del paquete que sea necesario; las dependencias (como py3-numpy) se cargan automáticamente.
# cargar Python científico
module load gnu12 py3-scipy
# verificar versión de Python y de los paquetes
python3 --version
python3 -c "import numpy, scipy; print(numpy.__version__, scipy.__version__)"
Ejemplo, ejemplo.py — encontrar el mínimo de una función con scipy.optimize:
# ejemplo.py
import numpy as np
from scipy import optimize
def f(x):
return (x - 3)**2 + 1
resultado = optimize.minimize(f, x0=0.0)
print(f"mínimo en x = {resultado.x[0]:.4f}, valor = {resultado.fun:.4f}")
Posible script para enviar a correr py_serie.sh:
#!/bin/bash
#SBATCH --job-name=python_serie
#SBATCH --output=python_mpi_%j.out
#SBATCH --partition=corta
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --time=00:05:00
module load gnu12 py3-scipy
python3 ejemplo.py
Y envia a correr, como ya sabemos, con sbatch py_serie.sh
Python paralelo con mpi4py
Para usar MPI desde Python, se carga además openmpi4 y el módulo py3-mpi4py, y se lanza el script con srun igual que un ejecutable compilado.
# ejemplo_mpi.py
from mpi4py import MPI
comm = MPI.COMM_WORLD
print(f"proceso {comm.Get_rank()} de {comm.Get_size()}")
#!/bin/bash
#SBATCH --job-name=python_mpi
#SBATCH --output=python_mpi_%j.out
#SBATCH --partition=compute
#SBATCH --nodes=1
#SBATCH --ntasks=8
#SBATCH --time=00:30:00
module load gnu12 openmpi4 py3-mpi4py
srun python3 ejemplo_mpi.py
Instalación de paquetes propios: Los módulos py3-* son instalados en un árbol de solo lectura, así que no admiten pip install directo. Para paquetes adicionales requeridos por el usuario, se debe crear un entorno propio con python3 -m venv ~/mi_entorno --system-site-packages después de cargar los módulos que sean necesarios como base, se activa con source ~/mi_entorno/bin/activate, y se instala allí con pip install lo que falte.
Elegir nodos específicos
Para fijar en qué geofisicaN corre el trabajo, se utiliza --nodelist:
#SBATCH --nodelist=geofisica1,geofisica2
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=8
Sesión interactiva
Para probar comandos a mano en un nodo de cómputo, sin escribir un script:
salloc --nodes=1 --ntasks=8 \
--time=00:30:00
# al entrar en la asignación:
srun --pty bash
Vigilar el trabajo. Después de sbatch, se utiliza squeue -u $USER para ver el estado (PD = pendiente, R = corriendo), y se revisan los archivos *_%j.out/*_%j.err (donde %j es reemplazado por el número de trabajo) para la salida y los errores.
Enviar el trabajo y revisar la salida
sbatch mi_script.sh
squeue -u $USER
cat mi_trabajo_.out # salida estándar del programa
cat mi_trabajo_.err # errores, si los hubo
Recursos (colas) Disponibles
Amplia - cola de uso general (por defecto)
Esta cola se utiliza para la mayoría de los trabajos cotidianos. Es la opción correcta salvo que el trabajo necesite más de 3 días, más de 4 nodos a la vez, o sea una prueba rápida que se quiera correr de inmediato.
- Límite de tiempo: 3 días (
72:00:00) - Tiempo por defecto si no lo especificás: 4 horas
- Nodos máximos por trabajo: 4 (de los 7 del cluster)
- Memoria: 3.8 GB por núcleo solicitado por defecto (se puede modificar con
--memo--mem-per-cpu) - Los núcleos son exclusivos: ningún trabajo comparte núcleo con otro
Se utilizan los ejemplos completos de la sección anterior con
#SBATCH --partition=amplia (ya vienen configurados así). Es importante
respetar el límite de --time (máximo 3 días) y de --nodes (máximo 4).
Corta — cola corta / de prueba (debug)
Para pruebas rápidas, depuración interactiva, o verificar que un script
funciona antes de enviarlo por más tiempo a amplia o larga. Al estar
limitada a 2 horas, tiene mayor prioridad que amplia, de modo que
los trabajos cortos no queden esperando detrás de trabajos largos.
- Límite de tiempo: 2 horas
- Tiempo por defecto si no lo especificás: 30 minutos
- Nodos máximos por trabajo: 1
- Memoria: 3.8 GB por núcleo solicitado por defecto
Para pruebas rápidas, lo más práctico suele ser una sesión interactiva en lugar de un script:
srun -p corta --nodes=1 --ntasks=4 --cpus-per-task=1 --pty bash
Dentro de esa sesión interactiva también es necesario cargar
los módulos necesarios (module load ...) antes de correr el programa.
También es posible usar cualquiera de los scripts completos de la sección
anterior, cambiando --partition=corta y ajustando --time a un
máximo de 2 horas y --nodes a 1.
Larga — cola de trabajos de larga duración
Para trabajos que realmente necesitan más de 3 días. Está limitada a un solo nodo para que un trabajo muy largo no ocupe gran parte del cluster durante semanas — si el trabajo necesita más de 8 núcleos/32GB, se recomienda evaluar si puede reestructurarse, o consultar con el administrador de Quipu
- Límite de tiempo: 14 días
- Tiempo por defecto si no lo especificás: 1 día
- Nodos máximos por trabajo: 1
- Memoria: 3.8 GB por núcleo solicitado por defecto
Se utilizan los scripts completos de la sección anterior con
#SBATCH --partition=larga y #SBATCH --nodes=1, ajustando --time
hasta un máximo de 14 días.
Cómo funciona la planificación entre colas
Las tres particiones comparten los mismos 7 nodos físicos — no hay hardware dedicado por cola. Cuando varios trabajos esperan recursos, el planificador de Slurm (backfill) decide qué ejecutar a continuación según:
- Prioridad de partición — corta (800) > amplia (500) > larga (300), por lo que, en igualdad de condiciones, los trabajos cortos se planifican antes que los largos cuando hay contención por los nodos.
- Tiempo de espera en cola — los trabajos pendientes más antiguos ganan prioridad con el tiempo, de modo que un trabajo no quede esperando indefinidamente solo porque sigan llegando trabajos más cortos.
- Backfill — si un trabajo más chico o más corto puede encajar en un hueco sin retrasar a otro de mayor prioridad, Slurm lo ejecuta antes en lugar de dejar nodos ociosos.
Una nota sobre la equidad entre usuarios
Actualmente el cluster no cuenta con contabilidad de recursos por usuario
(no hay límites tipo sacctmgr/QOS). En la práctica, esto significa que
los límites de las particiones detalladas arriba se aplican por
trabajo, no por usuario — nada impide técnicamente que una persona
envíe varios trabajos grandes a la vez. Por favor, sea considerado con el
resto de los usuarios: evite enviar muchos trabajos grandes a amplia
simultáneamente si no es necesario, y prefiera zcode>corta para pruebas, de
modo de no ocupar innecesariamente capacidad de amplia/larga. Si esto
se convierte en un problema recurrente, en el futuro se podrían
introducir límites por usuario.