ALG & Architectures Parallèles

Programmation parallèle

Modèles, synchronisation & comparaison des langages

C/C++ · OpenMP Rust Java Python
01 · Introduction

Même travail, plus de cœurs

1 cœur
t = 8
cœur 0
t = 2
cœur 1
cœur 2
cœur 3
×4plus rapide
CPU multicœur
GPU
Clusters
01 · Concepts fondamentaux

Processus vs thread

Processusapplication en cours

échange lourd Processus A mémoire A Processus B mémoire B PLANTÉ
isolérobusteéchanges lourds

Threadsous-unité d'un processus

Processus · Navigateur web mémoire partagée Affichage Téléchargement Clics souris 2 écritures en même temps !
mémoire partagéeultra-rapidedangereux
01 · Concepts fondamentaux

Tâche : le quoi, pas le comment

Tâches
Thread pool
Filtre photo · 4 zones
T0
T1
T2
Tâche 1filtrer ↖
Tâche 2filtrer ↗
Tâche 3filtrer ↙
Tâche 4filtrer ↘
↖
↗
↙
↘
4 tâches · 3 threads · le système répartit
02 · Les problèmes

Race condition : compteur++ × 2

02 · Les problèmes

Deadlock & famine

Interblocagechacun attend l'autre

détient attend détient attend A B R1 R2 BLOQUÉ

Faminetoujours doublé

ressource prioritaires prioritaires T attente : 0 s
02 · Les mécanismes

La boîte à outils

section critique

Mutex

un seul à la fois

race condition
attente N=3

Sémaphore

N jetons d'accès

pool limité de ressources
T zzZ signal() wait()

Condition

dormir jusqu'au signal()

attente active (100 % CPU)
lire +1 écrire x++ 1 instruction CPU

Atomique

une étape indivisible

compteurs, sans verrou

Barrière

rendez-vous collectif

phases de calcul
02 · Les solutions

Problème → remède

Race conditionaccès simultané
Mutexautour des variables partagées
Atomiquespour les compteurs simples
Deadlockattente circulaire
Ordre stricttoujours A avant B
Timeouttry_lock() · 10 ms puis on relâche
Faminejamais servi
File FIFOpremier arrivé, premier servi
Fair locksverrous équitables
03 · Bibliothèques de parallélisme

3 familles, 3 architectures

1 machine RAM cœur cœur cœur cœur

Mémoire partagée

multicœur local

OpenMPIntel TBBJavaC# TPLRust Rayon
CPU CPU CPU CPU messages

Mémoire distribuée

clusters · 1 RAM par machine

MPI

GPU massif

des milliers de petits cœurs

CUDAOpenCLSYCLWebGPU
04 · OpenMP en C/C++

Fork–Join : un #pragma suffit

BARRIÈRE IMPLICITE MAÎTRE FORK JOIN T0T1T2T3
#include <omp.h> void traiter(vector<double>& d) { #pragma omp parallel for for (int i = 0; i < n; ++i) d[i] *= 2.0; }
donnees[ ] · 1 000 000 éléments · 4 cœurs
T0
T1
T2
T3
0 → 249 999
250 000 → 499 999
500 000 → 749 999
750 000 → 999 999
04 · OpenMP en C/C++

Partager, privatiser, réduire

shared(y) · y += 1

y race !

private(x) · une copie par thread

x₀ x₁ x₂ x₃
#pragma omp parallel for reduction(+:somme)
#pragma ajouté petit à petit schedule(dynamic) 1 seule machine (sinon MPI) false sharing GPU limité
05 · Rust

Fearless concurrency

src/main.rs
let v = vec![1, 2, 3, 4]; let mut total = 0; v.par_iter().for_each(|x| { total += x; // écriture partagée ! });
$ cargo build
error[E0594]: cannot assign to `total`,
as it is a captured variable in a `Fn` closure
--> src/main.rs:4:5
error: could not compile `demo`
REFUSÉ À LA COMPILATION

La règle du borrow checker

&T
data
✓
&mut T
data
✓
les deux
data
✗
Sendtransférable à un autre thread
Syncpartageable entre threads
05 · Rust

Arc, Mutex & Rayon

Arc<Mutex<i32>> 0
Arc : plusieurs propriétaires · Mutex : un seul écrivain
// une seule modification : donnees.par_iter_mut() .for_each(|x| *x *= 2);

Work-stealing · les cœurs libres volent du travail

W0
W1
W2
W3
si ça compile, c'est thread-safe perf du C/C++, sans GC courbe d'apprentissage deadlocks toujours possibles
06 · Java

De synchronized aux threads virtuels

1996
Java 1.0

synchronized

un moniteur par objet

public synchronized void retirer(int montant) { … }
2004
Java 5 · java.util.concurrent
tryLock(2, s)abandon possible
ExecutorServicepool réutilisé
ConcurrentHashMapverrous fins
2023
Java 21 · Projet Loom

Threads virtuels

gérés par la JVM · ultra-légers

Executors .newVirtualThreadPerTaskExecutor()
06 · Java

Threads virtuels : des millions

Threads OSlourds · quelques milliers
Threads virtuelsgérés par la JVM
≈ millierslimite du système
0threads virtuels
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) { … }
serveurs web · I/O
API très riche code simple, perf async aucune sécurité à la compilation verbeux (CompletableFuture)
07 · Python

Le GIL : un seul à la fois

threading 1 GIL partagé

Thread 1
Thread 2
Thread 3
Thread 4
1 cœur actif à la fois

multiprocessing 1 GIL / processus

Proc. 1
Proc. 2
Proc. 3
Proc. 4
4 cœurs actifs
calculer_carre × 4 :~4 s→~1 sI/O : les threads suffisent (GIL relâché)
07 · Python

Contourner le GIL

concurrent.futures

haut niveau · moderne

ProcessPoolExecutor() .map(f, data)

multiprocessing

contrôle fin · Queue, Value

Pool(4).map(f, data)

joblib

data science · scikit-learn

Parallel(n_jobs=4)( delayed(f)(i) for i in …)
Le prix à payer
500 Mo500 Mo500 Mo500 Mo500 Mo500 Mo500 Mo500 Mo = 4 Go
8 processus = 8 interpréteurs en mémoire
{ objet }
b'\x80\x04\x95…'
{ objet }
pickle à chaque échange
if __name__ == '__main__':obligatoire (Windows / macOS)
Bientôt
3.13+Mode sans GILvrai parallélisme avec threading
3.14Sous-interpréteursInterpreterPoolExecutor
08 · Synthèse

Comparaison

C/C++ · OpenMPun #pragma, performance brute
Rust · Rayonsi ça compile, c'est sûr
JavaAPI riche, threads virtuels
Pythonsimple, mais freiné par le GIL
appréciation qualitative · d'après l'analyse présentée
08 · Conclusion

Quel outil pour quel besoin ?

Accélérer un code C/C++ existant

OpenMP

Sécurité garantie et performance native

Rust · Rayon

Serveur avec des millions de requêtes I/O

Java · threads virtuels

Data science et prototypage rapide

Python · joblib

Problème trop grand pour une machine

MPI · cluster

Calcul massif sur matrices

GPU · CUDA

Merci !

Des questions ?

→ / espace : avancer · ← : reculer · F : plein écran · N : notes & minuteur · R : rejouer