C++
Introducció
En aquest exemple, explorarem com enviar a Multivac un programa usant el mòdul omp per a paral·lelitzar i compilar un programa que obre diferents threads.
Crearem un arxiu per exemple anomenat test_openmp.c amb finalitats demostratives.
Codi d’exemple
#include <omp.h>
#include <stdio.h>
#include <unistd.h> // Per a la funció sleep()
int main() {
// Exemple 1: Threads s'executen sense ordre específic
printf("Exemple 1: Threads sense ordre específic\n");
// Variable compartida
int shared_data = 0;
#pragma omp parallel
{
int id = omp_get_thread_num();
// Cada thread modifica la variable compartida sense sincronització
++shared_data;
printf("Thread %d ha modificat shared_data a %d\n", id, shared_data);
// Simula una tasca
sleep(1);
// Cada thread llegeix la variable compartida
printf("Thread %d llegeix shared_data: %d\n", id, shared_data);
}
// Exemple 2: Threads es passen informació entre ells i s'esperen
printf("\nExemple 2: Threads amb comunicació i sincronització\n");
// Reinicialitzem la variable compartida
shared_data = 0;
#pragma omp parallel shared(shared_data)
{
int id = omp_get_thread_num();
int n = omp_get_num_threads();
for (int i = 0; i < n; ++i) {
#pragma omp barrier // Espera que tots els threads arribin aquí
if (id == i) {
// Thread actual modifica la variable compartida
++shared_data;
printf("Thread %d ha actualitzat shared_data a %d\n", id, shared_data);
sleep(1);
}
#pragma omp barrier // Espera que la modificació sigui visible per a tots els threads
// Tots els threads esperen en ordre per llegir la variable compartida
for (int j = 0; j < n; ++j) {
#pragma omp barrier
if (id == j) {
printf("Thread %d llegeix shared_data: %d\n", id, shared_data);
}
}
}
}
return 0;
}
Execució
Crearem un script amb extensió .slurm anomenat test_omp.slurm que li diu a Multivac com ha d’executar el codi.
VERSION=1.3
JOB_NAME=test
NAME_OUTPUT=out
PARTITION=all
N_TASKS=4
CPUS_PER_TASK=2
MAIL_TYPE=END,FAIL
MAIL_USER=alexandre.gracia@upc.edu
MEMORY=15000M # [K|M|G|T] log-c1: 3715M, log-c2:3779M, log-c3:3715M, log-c4:3779M, dops-a1:15879M, dops-a2:15879M, dops-a3:15878M, dops-a4:15900M, dops-a5:15900M, cetus:64166M, psi:257379M
BEGIN=now # "hh:mm","now+1hour", "now+60" (seconds by default), "2010-01-20T12:34:00" YYYY-MM-DD[THH:MM[:SS]]
TIME_LIMIT=23:59:00 # "minutes", "minutes:seconds", "hours:minutes:seconds", "days-hours", "days-hours:minutes", "days-hours:minutes:seconds"
LOG_OUTPUT=log
FORCED_NODES= # Nodes en els què es forçarà l'execució
EXCLUDED_NODES= # Nodes que seràn exclosos de l'execució
ROUTE=~/tests/test_omp # Ruta dels nostres scripts
COMMANDS=(
# Comandes d'informació
"hostname"
"whoami"
# Compilar el programa
"g++ -fopenmp test_openmp.c -o test_openmp"
# Ejecutar el programa
"./test_openmp"
)
Aquest script el llançarem des de iocex usant la següent comanda:
multivac test_omp.slurm
Output
La sortida del nostre script hauria de ser similar a aquesta. En el primer Exemple, els threads s’executen sense ordre concret. En el segon Exemple, els threads llegeixen una variable compartida i n’extreuen el seu contingut amb un ordre concret, esperant-se a l’script anterior.
dops-a3
alexandre.gracia
Exemple 1: Threads sense ordre específic
Thread 4 ha modificat shared_data a 1
Thread 1 ha modificat shared_data a 5
Thread 6 ha modificat shared_data a 7
Thread 0 ha modificat shared_data a 2
Thread 3 ha modificat shared_data a 6
Thread 5 ha modificat shared_data a 3
Thread 2 ha modificat shared_data a 4
Thread 7 ha modificat shared_data a 5
Thread 7 llegeix shared_data: 7
Thread 2 llegeix shared_data: 7
Thread 5 llegeix shared_data: 7
Thread 0 llegeix shared_data: 7
Thread 1 llegeix shared_data: 7
Thread 6 llegeix shared_data: 7
Thread 4 llegeix shared_data: 7
Thread 3 llegeix shared_data: 7
Exemple 2: Threads amb comunicació i sincronització
Thread 0 ha actualitzat shared_data a 1
Thread 0 llegeix shared_data: 1
Thread 1 llegeix shared_data: 1
Thread 2 llegeix shared_data: 1
Thread 3 llegeix shared_data: 1
Thread 4 llegeix shared_data: 1
Thread 5 llegeix shared_data: 1
Thread 6 llegeix shared_data: 1
Thread 7 llegeix shared_data: 1
Thread 1 ha actualitzat shared_data a 2
Thread 0 llegeix shared_data: 2
Thread 1 llegeix shared_data: 2
Thread 2 llegeix shared_data: 2
Thread 3 llegeix shared_data: 2
Thread 4 llegeix shared_data: 2
Thread 5 llegeix shared_data: 2
Thread 6 llegeix shared_data: 2
Thread 7 llegeix shared_data: 2
Thread 2 ha actualitzat shared_data a 3
Thread 0 llegeix shared_data: 3
Thread 1 llegeix shared_data: 3
Thread 2 llegeix shared_data: 3
Thread 3 llegeix shared_data: 3
Thread 4 llegeix shared_data: 3
Thread 5 llegeix shared_data: 3
Thread 6 llegeix shared_data: 3
Thread 7 llegeix shared_data: 3
Thread 3 ha actualitzat shared_data a 4
Thread 0 llegeix shared_data: 4
Thread 1 llegeix shared_data: 4
Thread 2 llegeix shared_data: 4
Thread 3 llegeix shared_data: 4
Thread 4 llegeix shared_data: 4
Thread 5 llegeix shared_data: 4
Thread 6 llegeix shared_data: 4
Thread 7 llegeix shared_data: 4
Thread 4 ha actualitzat shared_data a 5
Thread 0 llegeix shared_data: 5
Thread 1 llegeix shared_data: 5
Thread 2 llegeix shared_data: 5
Thread 3 llegeix shared_data: 5
Thread 4 llegeix shared_data: 5
Thread 5 llegeix shared_data: 5
Thread 6 llegeix shared_data: 5
Thread 7 llegeix shared_data: 5
Thread 5 ha actualitzat shared_data a 6
Thread 0 llegeix shared_data: 6
Thread 1 llegeix shared_data: 6
Thread 2 llegeix shared_data: 6
Thread 3 llegeix shared_data: 6
Thread 4 llegeix shared_data: 6
Thread 5 llegeix shared_data: 6
Thread 6 llegeix shared_data: 6
Thread 7 llegeix shared_data: 6
Thread 6 ha actualitzat shared_data a 7
Thread 0 llegeix shared_data: 7
Thread 1 llegeix shared_data: 7
Thread 2 llegeix shared_data: 7
Thread 3 llegeix shared_data: 7
Thread 4 llegeix shared_data: 7
Thread 5 llegeix shared_data: 7
Thread 6 llegeix shared_data: 7
Thread 7 llegeix shared_data: 7
Thread 7 ha actualitzat shared_data a 8
Thread 0 llegeix shared_data: 8
Thread 1 llegeix shared_data: 8
Thread 2 llegeix shared_data: 8
Thread 3 llegeix shared_data: 8
Thread 4 llegeix shared_data: 8
Thread 5 llegeix shared_data: 8
Thread 6 llegeix shared_data: 8
Thread 7 llegeix shared_data: 8
El resultat del nostre programa un cop finalitzada l’execució serà visible al mateix directori a on hem fet l’execució.
Com podem observar en el primer cas, com que els threads no s’esperen, l’ordre és aleatori, i cadascun dels threads llegeix la variable respecte el seu ordre però no assegurant el valor de la variable respecte el que correspon, acabant un resultat inesperat.