M1

OpenMP

Pour utiliser OpenMP, il faut:

  #include <omp.h>

Les blocs #pragma

Pour paralléliser l'exécution d'un bloc :

#pragma omp parallel
{
  // Bloc
}

OpenMP exécutera le bloc autant de fois qu'il y a de threads disponibles sur le système.

Pour paralléliser l'exécution d'une boucle for :

#pragma omp parallel for
for (i = 0; i < 10; i++)
  // ...

Pour définir une variable privée pour chaque bloc parallélisé :

#pragma omp parallel private(somme)
{
  somme = ...;
  // ...
}

#pragma omp parallel for private(tmp)
for (/* ... */) {
  tmp = ...;
}

Pour choisir le nombre de threads à utiliser :

omp_set_num_threads(10);

Pour réduire une variable à une seule valeur :

#pragma omp parallel for reduce(+:somme)
for (/* ... */)

Ici, le for va être parallélisé et chaque thread va calculer une partie de la somme. La réduction permet d'assembler toutes les valeurs calculé en une seule. Puisqu'il s'agit d'une somme ici, on utilise + mais on peut faire la même chose avec une soustraction (-), du calcul booléen (&& ou ||), etc.

Mesure le temps d'exécution

double wtime;

wtime = omp_get_wtime();

// ...

wtime = omp_get_wtime() - wtime;

Cette fonction peut être utilisé dans un bloc pragma ou bien à l'extérieur.

Sections

#pragma omp parallel private(i)
{
  #pragma omp sections
  {
    #pragma omp section
    for (i = 0; i < 10000; i++)
      // ...

    # pragma omp section
    for (i = 0; i < 10000; i++)
      // ...
  }
}

Ici, les deux fors s'éxecuteront en même temps. Contrairement à parallel, qui va essayer d'exécuter le code sur tous les threads disponibles, section utilise un seul thread par section créée (ici, 1 thread est utilisé pour le premier for et 1 autre pour le second).

Synchronisation entre les threads

Quand deux threads ont une directive nowait (c'est à dire que le code suivant ce bloc peut être exécuté avant la fin de ce bloc), on peut utiliser barrier pour synchroniser les threads.

#pragma omp parallel
{
  #pragma omp for nowait
  for (/* ... */)
    // Boucle 1
  #pragma omp for nowait
  for (/* ... */)
    // Boucle 2

  #pragma omg barrier
  printf("Eh bien le bonjour !");
}

Ici, la boucle 1 et la boucle 2 peuvent tourner en parallèle mais le printf ne sera fait qu'une fois la boucle 1 et 2 finies.

Sections critiques

Les sections critiques permettent au sein d'un bloc parralélisé de s'assurer qu'une instruction ne sera exécuté que par un seul thread à la fois :

#pragma omp for
  for (i = 0; i < 1000; i++) {
    #pragma omp critical
      if (tab[i] < min)
        min = tab[i]
  }

Ici, on évite que deux threads écrasent la valeur de min en même temps et que min n'ait pas la valeur du plus petit élément du tableau.