Laboratorul 1 - Introducere în clusterul HPC al UPB

Scopul laboratorului

Scopul acestui laborator este de a vă familiariza cu infrastructura de calcul de înaltă performanță (HPC – High Performance Computing) a facultății.

Concret, veți învăța:

  • cum să vă conectați la cluster prin nodul de acces (FEP - Front-End Processor),
  • cum să investigați resursele disponibile (partiții, CPU-uri, GPU-uri, memorie),
  • cum să alocați resurse și să rulați joburi prin SLURM,
  • cum să compilați și să rulați programe multi-threaded și CUDA pe cluster.

La finalul laboratorului ar trebui să puteți lansa un job pe cluster fără ajutor.

Ce este un cluster HPC?

Aplicațiile moderne din știință și inginerie precum simulări numerice, antrenarea modelelor de inteligență artificială, procesare Big Data, etc. necesită resurse de calcul care depășesc cu mult capacitatea unui singur calculator.

Un cluster HPC rezolvă această problemă combinând puterea a zeci sau sute de noduri de calcul interconectate printr-o rețea internă de mare viteză (de obicei InfiniBand sau Ethernet RDMA, de ordinul sutelor de Gbps).

Fiecare nod este un server echipat cu:

  • procesoare multi-core (CPU);
  • memorie RAM extinsă;
  • frecvent, acceleratoare grafice (GPU) optimizate pentru calcule masiv paralele.

Pe lângă GPU-uri, în infrastructurile HPC moderne apar tot mai frecvent acceleratoare specializate:

  • TPU (Tensor Processing Unit) – optimizate pentru antrenarea și inferența rețelelor neuronale;
  • DPU (Data Processing Unit) – dedicate procesării datelor și operațiilor de rețea (offloading);
  • NPU (Neural Processing Unit) – orientate spre inferență AI eficientă energetic, inclusiv pe dispozitive edge;
  • QPU (Quantum Processing Unit) – utilizate în calculatoarele cuantice.

Componentele principale ale unui cluster HPC sunt:

  • Noduri de acces (FEP) - servere prin care utilizatorii se conectează la cluster. Aici doar pregătiți codul și lansați joburi (NU se rulează calcule intensive).
  • Noduri de calcul - serverele pe care rulează efectiv joburile utilizatorilor (CPU-uri, GPU-uri, RAM extins).
  • Sistem de fișiere partajat — sistem de fișiere paralel (Lustre, BeeGFS, Ceph) accesibil de pe toate nodurile.
  • Scheduler de joburi — software-ul care primește cererile utilizatorilor și alocă resursele. Clusterul UPB folosește SLURM (Simple Linux Utility for Resource Management).

Nodurile de calcul sunt organizate în partiții — grupuri de noduri cu configurație hardware omogenă. Partițiile sunt, în schimb, heterogene între ele (de exemplu: o partiție doar cu CPU-uri, una cu GPU-uri A100, alta cu H100, etc.).

Aplicațiile rulate pe un cluster HPC folosesc paradigme de programare distribuită și paralelă, precum:

  • MPI – comunicare între noduri (distributed memory),
  • OpenMP/Pthreads – paralelism în cadrul unui nod multi-core (shared memory la nivel de CPU),
  • CUDA – programare pe GPU (shared memory la nivel de GPU).

Modelele pot fi, bineînțeles, combinate (programe hibride MPI+OpenMP+CUDA). Prin combinarea acestor tehnologii, sistemele HPC ating performanțe de ordinul tera– sau peta–flopilor, fiind esențiale în cercetarea științifică modernă.

Clusterul HPC al UPB

Universitățile tehnice dispun adesea de clustere HPC proprii, folosite pentru activități de cercetare și educație. Universitatea Politehnica din București deține un astfel de cluster modern, echipat cu GPU-uri performante (precum NVIDIA H100, A100).

Exemplu de afișare a partițiilor:

[nume.student@fep10 ~]$ sinfo -o "%10P %55N %8c %10m %20G"
PARTITION  NODELIST                                                CPUS     MEMORY     GRES
dgxa100    dgxa100-ncit-wn[01-04]                                  256      2063510    gpu:tesla_a100:8
dgxh100    dgxh100-precis-wn[01-03]                                224      1998908+   gpu:tesla_h100:8    
haswell*   haswell-wn[29-42]                                       32       127309     (null)
hd         xl675dg10-wn175                                         96       773225     gpu:tesla_a100:10
ml         sprmcrogpu-wn[140-141]                                  112      128224     gpu:tesla_a100:2
sprmcrogpu sprmcrogpu-wn13                                         64       515093     gpu:rtx_2080ti:8
ucsx       ucsx-ncit-gpu-wn100                                     64       257158     gpu:tesla_a100:3
xl         xl270-wn[161-162]                                       56       257138     gpu:tesla_p100:2

Asteriscul din dreptul partiției (haswell* în cazul nostru) indică partiția implicită. Dacă nu specificați o partiție, SLURM va aloca jobul pe partiția implicită.

Specificații CPU:

Partiție CPU Arhitectura An lansare Sockets Cores/Socket Threads/Core Total CPUs (threads) Frecvență per core (bază / boost) RAM/nod
dgxh100 Intel Xeon Platinum 8480C Sapphire Rapids 2023 2 56 2 224 până la 3.8 GHz ~2 TB
dgxa100 AMD EPYC 7742 Zen 2 (Rome) 2019 2 64 2 256 până la 3.4 GHz ~2 TB
ucsx Intel Xeon Gold 6326 Ice Lake 2021 2 16 2 64 2.9 / 3.5 GHz ~256 GB
xl Intel Xeon E5-2680 v4 Broadwell 2016 2 14 2 56 2.4 / 3.3 GHz ~256 GB
haswell Intel Xeon E5-2640 v3 Haswell 2014 2 8 2 32 2.6 / 3.4 GHz ~128 GB

Specificații GPU:

Partiție GPU Arhitectura An lansare Nuclee (cores) Memorie (VRAM)
dgxh100 NVIDIA H100 80GB Hopper 2022 14592 CUDA cores + 456 Tensor cores (Gen 4) 80 GB HBM3
dgxa100 NVIDIA A100-SXM4-80GB Ampere 2020 6912 CUDA cores + 432 Tensor cores (Gen 3) 80 GB HBM2e
ucsx NVIDIA A100-PCIE-40GB Ampere 2020 6912 CUDA cores + 432 Tensor cores (Gen 3) 40 GB HBM2e
xl NVIDIA Tesla P100 16GB Pascal 2016 3584 CUDA cores 16 GB HBM2

Ce este FEP-ul?

FEP-ul reprezintă nodul de acces către clusterul HPC al universității. Pe scurt, este serverul prin intermediul căruia utilizatorii se conectează la infrastructură pentru a pregăti și lansa job-urile pe resursele de calcul ale clusterului.

FEP-ul are un rol limitat, oferind:

  • un mediu de lucru minimal (shell Linux),
  • acces la fișierele proprii,
  • pregătirea codului sursă,
  • lansarea joburilor către nodurile de calcul din cluster.

Execuția efectivă a joburilor are loc exclusiv pe nodurile de calcul, care dispun de resursele reale de procesare (CPU, GPU, RAM extins).

Va rugăm NU rulați aplicațiile direct PE FEP! Trimiteți-le ca joburi către cluster, folosind SLURM. FEP-ul este un nod comun de acces pentru toți utilizatorii, iar rularea de programe intensive pe el afectează toți utilizatorii conectați.

Conectarea la FEP se realizează prin SSH:

 ssh -X -o ServerAliveInterval=100 user.name@fep.grid.pub.ro 

Explicația parametrilor:

  • -X – permite redirecționarea interfeței grafice (dacă folosiți aplicații GUI);
  • ServerAliveInterval=100 – menține conexiunea activă dacă terminalul e inactiv o perioadă.

Pentru o conectare mai simplă, puteți adăuga, local, în fișierul ~/.ssh/config, următorul bloc (după ce ați generat o pereche cheie privată/publică, de exemplu RSA):

Host fep fep.grid.pub.ro
    HostName fep.grid.pub.ro
    User <user.name>
    ForwardX11 yes
    IdentityFile ~/.ssh/id_fep
    ServerAliveInterval 100

Apoi, copiați cheia publică în fișierul ~/.ssh/authorized_keys de pe FEP, pentru a vă putea conecta de acum direct prin ssh fep. După conectare, utilizatorul se află pe serverul FEP.

Pentru o experiență confortabilă, recomandăm montarea sistemului de fișiere de pe FEP pe calculatorul local folosind sshfs (disponibil pe macOS, Linux și WSL) sau Rclone (pe Windows). Astfel, puteți edita codul cu un IDE local (e.g. VS Code) și compila/rula pe cluster prin SSH.

Configurarea mediului de lucru pe cluster ​

Pe un cluster sunt instalate simultan mai multe versiuni de compilatoare și biblioteci (GCC, CUDA, OpenMPI, etc.). Pentru a evita conflicte și pentru a permite fiecărui utilizator să aleagă versiunile dorite, clusterele HPC folosesc Environment Modules — un sistem care modifică temporar variabilele de mediu (PATH, LD_LIBRARY_PATH) la cerere. Astfel, utilizatorul poate folosi exact versiunea dorită a unui compilator sau a unei biblioteci, fără a afecta sistemul global.

Comenzi uzuale:

module help
module avail                      # afișează modulele disponibile
module load libraries/cuda-13.0   # încarcă biblioteca CUDA corespunzătoare
module list                       # arată modulele active în sesiunea curentă
module unload libraries/cuda-13.0 # dezactivează un modul
module purge                      # dezactivează toate modulele încărcate

Utilizare SLURM ​

SLURM este scheduler-ul de joburi folosit pe clusterul UPB. Rolul său este să primească cererile utilizatorilor, să le pună în coadă și să aloce resursele disponibile (CPU-uri, GPU-uri, memorie) în mod echitabil, ținând cont și de restricțiile fiecărei partiții. De exemplu, anumite partiții sunt rezervate exclusiv unor echipe de cercetare și sunt accesibile doar membrilor acestora. Altele pot impune limite de timp per job (e.g. maximum 10 minute pentru conturile de studenți), un număr maxim de job-uri rulate simulat, un număr maxim de noduri folosite per job, maxim RAM per job, etc.

Mai jos sunt două programe pe care le vom folosi pe parcursul laboratorului pentru a înțelege cum funcționează alocarea resurselor în SLURM.

Program Pthreads (paralelism pe CPU):

hello_pthreads.c
#include <stdio.h>
#include <pthread.h>
 
void* thread_func(void* arg) {
    int id = *(int*)arg;
    printf("Hello from thread %d\n", id);
    return NULL;
}
 
int main() {
    int N = 8;
    pthread_t threads[N];
    int ids[N];
    for (int i = 0; i < N; i++) {
        ids[i] = i;
        pthread_create(&threads[i], NULL, thread_func, &ids[i]);
    }
    for (int i = 0; i < N; i++)
        pthread_join(threads[i], NULL);
    return 0;
}

Program CUDA (paralelism pe GPU):

hello_cuda.cu
#include <stdio.h>
 
__global__ void hello() {
    printf("Hello from GPU thread %d\n", threadIdx.x);
}
 
int main() {
    hello<<<1, 4>>>();
    cudaDeviceSynchronize();
    printf("CUDA test done!\n");
    return 0;
}

Pentru a rula aceste programe pe cluster, trebuie să le trimiteți ca job-uri folosind SLURM, specificând resursele necesare. De exemplu, programul CUDA va fi nevoie să îl rulăm pe o partiție care conține GPU (cum ar fi xl, ucsx), în timp ce pentru programul C este de ajuns haswell.

În mod implicit, SLURM alocă 1 CPU logic și 0 GPU-uri pentru orice job. Dacă programul vostru are nevoie de mai multe resurse (mai multe CPU-uri, GPU, memorie suplimentară), trebuie să le cereți explicit. Altfel, un program care are nevoie de N thread-uri logice va rula pe 1 singur thread hardware, iar un program care are nevoie de CUDA nu va merge deloc, chiar dacă codul în sine este corect.

SLURM oferă un control mult mai granular asupra resurselor hardware decât simpla specificare a numărului de CPU-uri sau GPU-uri. De exemplu:

  • distribuția proceselor MPI pe socket-uri (--ntasks-per-socket);
  • memory binding NUMA — procesele alocă și accesează memorie doar din nodul NUMA local, reducând latența (--cpu-bind, --mem-bind);
  • activarea sau dezactivarea SMT (--hint=nomultithread).

Aceste opțiuni devin relevante când scrieți aplicații hibride MPI+OpenMP+CUDA și doriți să vă folosiți la maximum de arhitectura hardware a nodului de calcul.

Există două moduri de a lansa joburi pe cluster: sbatch și srun.

sbatch trimite un job în coada SLURM și returnează imediat controlul — jobul rulează asincron, în fundal. Outputul nu apare în terminal, ci se salvează în fișierul specificat prin --output (sau, implicit, slurm-<JOB_ID>.out). Job-ul continuă chiar dacă închideți terminalul. Este modul recomandat pentru aplcații reale, a căror rulare poat dura de la ore până la zile.

srun lansează comanda sincron — terminalul se blochează și așteaptă finalizarea, iar outputul apare direct în consolă. Dacă închideți terminalul, job-ul se oprește. Este util pentru testare rapidă sau sesiuni interactive (srun –pty bash).

sbatch srun
Mod execuție Asincron (fundal) Sincron (blocant)
Output Fișiere .out / .err Direct în terminal
La închiderea terminalului Jobul continuă Jobul se oprește
Utilizare tipică Aplicații complexe Testare, debugging, interactiv

Exemplu de script sbatch pentru hello_pthreads.c:

hello_pthreads.sh
#!/bin/bash
#SBATCH --cpus-per-task=8
gcc -o hello_pthreads hello_pthreads.c -lpthread
./hello_pthreads

Exemplu de script sbatch pentru hello_cuda.cu:

hello_cuda.sh
#!/bin/bash
#SBATCH --partition=dgxa100
#SBATCH --gres=gpu:1
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
nvcc -o hello_cuda hello_cuda.cu
./hello_cuda

Apoi rulați sbatch hello_pthreads.sh, sbatch hello_cuda.sh pentru a submite jobul.

În prezent, modulefile-urile nu sunt disponibile pe cluster. Până la rezolvarea acestei probleme, configurarea mediului CUDA se face manual prin variabilele de mediu, ca în exemplul de mai sus (export PATH=… și export LD_LIBRARY_PATH=…). Când modulefile-urile vor fi disponibile, cele două linii de export vor fi înlocuite cu module load libraries/cuda-13.0.

Comenzi uzuale SLURM:

sbatch hello_cuda.sh                           # submitere job; returnează un job_id
sbatch --partition=ucsx hello_cuda.sh          # override partiție din linia de comandă
srun --partition=xl --gres=gpu:1 ./hello_cuda  # rulare interactivă simplă
srun --partition=xl --gres=gpu:1 --pty bash    # sesiune interactivă pe nodul de calcul
 
sinfo                                              # afișare simplă
sinfo -o "%20P %6a %8D %8c %10m %20G %10l %8t %N"  # format detaliat
sinfo -o '%9P %4c %8z %8X %8Y %8Z'                 # distribuție sockets/cores/threads per partiție
 
squeue                          # afișare joburi
squeue --me                     # afișare doar joburile tale
squeue -p xl --state=R --format="%.8i %.10P %.15u %.10T %.10M %.8C %.10m %.5b %.20R"  # joburile active pe o partiție, format detaliat
 
scancel <job_id>                # oprește un job specific
scancel --me                    # oprește TOATE joburile tale
 
scontrol show partition <nume>  # detalii complete despre o partiție
scontrol show node <nume>       # detalii despre un nod specific
scontrol show job <job_id>      # detalii și stare completă a unui job
 
sacctmgr show user $USER withassoc format=account,user,partition,MaxWallDurationPerJob  # timp maxim de rulare permis per partiție

Exerciții

  • Creați-vă un document în care să vă treceți toate informațiile pe care le-ați putut obține despre cluster (ex: arhitecturi existente, permisiuni de rulare a joburilor, etc.). Exemple de întrebări din partea asistenților de laborator:
    • Care sunt partițiile existente pe cluster? Cu ce diferă una față de alta? Pe ce partiții aveți acces? (hint: scontrol show) Cât RAM e disponibil vs. maximul permis per job (hint: scontrol show)?
    • Rulați pe clustere CPU comanda lscpu și pe clustere GPU comanda nvidia-smi. Ce informații ați putut extrage despre CPU/GPU?
    • Cum vedeți joburile care se execută în prezent pe cluster? Cum opriți un job specific?
  • Rulați cel puțin un program C multi-threaded și un program CUDA, atât cu sbatch, cât și cu srun. Verificați rezultatele. Care este diferența dintre cele două comenzi?
  • Scrieți un program C multi-threaded care face calcule intensive (de exemplu suma unei serii pe fiecare thread). Rulați-l o dată cu --cpus-per-task=N și o dată fără --cpus-per-task=N, unde N este numărul de thread-uri din program. Comparați timpii de execuție și explicați diferența.

Resurse utile

asc/laboratoare/01.txt · Last modified: 2026/03/03 09:07 by tudor.calafeteanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0