SlideShare une entreprise Scribd logo
1  sur  22
Télécharger pour lire hors ligne
Fork / Join
Rémi Forax
Vocabulaire
Concurrence
– Exécuter plusieurs calculs en même temps
Parallèle
– Un calcul est décomposé et exécuté sur plusieurs threads
Vectorisé
– Une opération simple (+, min, etc) est exécutée par un même
thread sur plusieurs données adajcences en mémoire
(sur 128 bits, 256 bits, etc)
Distribué
– Un calcul est décomposé et exécuté sur plusieurs machines
Start / Join
Historiquement, le modèle utilisé est start / join
Exemple, faire *2 aux valeurs d’un tableau avec 5 threads
var threads = IntStream.range(0, 5)
.mapToObj(i -> new Thread(() -> {
var startIndex = ...
var endIndex = ...
for(var i = startIndex; i < endIndex; i++) {
array[i] *= 2;
}
}))
.toList();
for(var thread: threads) { thread.start(); }
for(var thread: threads) { thread.join(); }
Recopié du modèle fork() / waitpid() sous Unix
Le modèle start / join
Avec un tableau de 48 cases
[0-9[ [9-18[ [18-28[ [28-38[ [38-48[
start
start
start
start
start
join join join join join
0 48
Problèmes du modèle start / join
Le modèle start / join marche bien
– si toutes les données sont connus en amont
(upfront)
Marche bien pour un tableau, moins bien pour un arbre
rouge/noir car il faut tout parcourir pour voir toutes les
données
– si le calcul est homogène en temps
Si le calcul est long seulement pour certain index, une
thread peut être encore entrain de faire des calculs alors
que les autres ont finis
=> problème de répartition de la charge de calcul
Divide and Conquer
Solution naive “divide and conquer”,
évite d’avoir à connaitre tout les données en amont
process(int startIndex, int endIndex)
if endIndex – startIndex < SMALL
// for(...)
return
var middle = (startIndex + endIndex) >>> 1;
start process(startIndex, middle);
start process(middle, endIndex);
join
join
}
Fork / Join
Evolution de la solution naive “divide and conquer”,
on évite d’avoir des threads en attentes
process(int startIndex, int endIndex
if endIndex – startIndex < SMALL
// for(...)
return ...
var middle = (startIndex + endIndex) >>> 1;
fork process(startIndex, middle);
var result2 = process(middle, endIndex);
var result1 = join
return combine result1 result2
}
Le modèle fork / join
Avec un tableau de 48 cases
[0-24[ [24-48[
0 48
[0-12[ [12-24[ [24-36[ [36-48[
[0-6[ [6-12[
join
join
join
join
Problème du modèle fork / join
On a toujours le problème de la répartition de la
charge de calcul si le calcul est pas homogène
en temps
Solution: découpler la partie de calcul effectuer
de la thread qui effectue le calcul
=> pool de thread + work stealing
Thread Pool avec Work Stealing
●
Le nombre de threads est fixe
●
Chaque thread à sa queue de tâche
●
Si un thread a fini toutes les tâches de sa queue
(si la queue est vide), il va voler (steal) des tâches
à la fin dans les queues des autres threads
=> Le fait d’aller voler des tâches permet
d’équilibrer la charge de travail
fork / join + work stealing
Avec un tableau de 48 cases
[0-24[ [24-48[
0 48
[0-12[ [12-24[ [24-36[ [36-48[
[0-6[ [6-12[
[12-18[ [18-24[ [24-30[ [30-36[ [36-42[ [42-48[
steal
submit
submit submit submit submit submit submit submit
Régler fork / join
Sélection de SMALL
– Si SMALL trop gros, pas assez de tâches, pas assez de
parallèlisme
– Si SMALL trop petit, trop de tâches, trop d’allocations +
queue trop grande
process(int startIndex, int endIndex
if endIndex – startIndex < SMALL
// for(...)
return ...
...
}
Régler fork / join (2)
Combine doit être une opération associative
On ne peut pas décomposer le calcul
si combine(combine(a, b), c) != combine (a, combine(b, c))
process(int startIndex, int endIndex
...
var middle = (startIndex + endIndex) >>> 1;
fork process(startIndex, middle);
var result2 = process(middle, endIndex);
var result1 = join
return combine result1 result2
}
La moyenne (a, b) → (a + b) / 2 est pas associative !
Fork / Join en Java
java.util.concurrent.ForkJoinPool
Le pool de threads (l’ExecutorService) qui fait
du work stealing s’appel ForkJoinPool
Pour créer un ForkJoinPool
– new ForkJoinPool(5) // avec 5 threads
Il existe déjà un ForkJoinPool par défaut,
– ForkJoinPool.commonPool()
Tâches du ForkJoinPool
Le ForkJoinPool exécute des ForkJoinTask
– RecursiveAction pour les tâches sans valeur
– RecursiveTask<V> pour les tâches avec une valeur
Exécuter la tâche initiale sur le ForkJoinPool
– execute(RecursiveAction)
●
L’exécution est asynchrone
– invoke(RecursiveAction|RecursiveTask)
●
Bloque la thread courante et attend l’exécution de la tâche
– submit(RecursiveAction|RecursiveTask) → Future
●
Renvoie un Future (Future.get() permet d’avoir la valeur)
Rappel sur la suite de Fibonacci
Calcul de la suite de Fibonacci en récursif
(stupide mais simple)
int fibonacci(int n)
if (n <= 1) {
return n;
}
return fibonacci(n – 1) + fibonacci(n – 2);
}
Idée: on peut utiliser le pattern Fork / Join pour
faire le calcul en parallèle
Exemple de RecursiveTask
Calcul de Fibonacci en récursif (stupide mais simple)
private static class Fibonacci extends RecursiveTask<Integer> {
private final int n;
private Fibonacci(int n) { this.n = n; }
@Override
protected Integer compute() {
if (n <= 1) {
return 1;
}
var fib1 = new Fibonacci(n - 1);
var fib2 = new Fibonacci(n - 2);
f1.fork(); // soumet au ForkJoinPool
var result2 = fib2.compute(); // calcul dans la thread courante
var result1 = f1.join(); // attend le résultat de la tâche
return result1 + result2; // le + est bien associatif
}
}
Exemple de RecursiveTask (2)
Calcul de Fibonacci en récursif (stupide mais simple)
private static class Fibonacci extends RecursiveTask<Integer> {
private final int n;
private Fibonacci(int n) { this.n = n; }
@Override
protected Integer compute() {
…
}
}
…
var pool = ForkJoinPool.commonPool();
var fibo = new Fibonacci(15);
var result = pool.invoke(fibo); // on attend la fin du calcul
Exemple de RecursiveAction
Multiplier par 2 les valeurs du tableaux
private static class MultiplyBy2 extends RecursiveAction {
private final int[] array;
private final int start;
private final int end;
private MultiplyBy2(...) { this.array = array; this.start = start; this.end = end; }
@Override
protected void compute() {
if (end – start < 1024) {
for(var i = start; I < end; i++) { array[i] *= 2; }
return;
}
var middle = (start + end) >>> 1;
var mul1 = new MultiplyBy2(array, start, middle);
var mul2 = new MultiplyBy2(array, middle, end);
mul2.fork();
mul1.compute();
mul2.join();
}
}
Exemple de RecursiveAction (2)
Multiplier par 2 les valeurs du tableaux
private static class MultiplyBy2 extends RecursiveAction {
private final int[] array;
private final int start;
private final int end;
private MultiplyBy2(...) { this.array = array; this.start = start; this.end = end;
}
@Override
protected void compute() {
...
}
}
…
var pool = ForkJoinPool.commonPool();
var fibo = new MultiplyBy2(array, 0, array.length);
var future = pool.submit(fibo); // on récupère un future
…
System.out.println(future.get()); // on récupère la valeur
Relation avec les Streams
Lorsque l’on utilise un Stream parallèle
stream.parallel(). …
le calcul est fait dans le ForkJoinPool par défaut
ForkJoinPool.commonPool()
L’implantation utilise la méthode
Spliterator.trySplit() du Spliterator du Stream
pour faire la décomposition récursive

Contenu connexe

Similaire à 17-Concurrence-Fork-Join.pdf

Javascript les générateurs (generators)
Javascript   les générateurs (generators)Javascript   les générateurs (generators)
Javascript les générateurs (generators)Julien CROUZET
 
ALF 11 - Diagramme de flux de contrôle et WebAssembly
ALF 11 - Diagramme de flux de contrôle et WebAssemblyALF 11 - Diagramme de flux de contrôle et WebAssembly
ALF 11 - Diagramme de flux de contrôle et WebAssemblyAlexandru Radovici
 
Java 7 - Fork/Join
Java 7 - Fork/JoinJava 7 - Fork/Join
Java 7 - Fork/JoinZenika
 
ALT.NET Modéliser Parallèle avec C# 4.0
ALT.NET Modéliser Parallèle avec C# 4.0ALT.NET Modéliser Parallèle avec C# 4.0
ALT.NET Modéliser Parallèle avec C# 4.0Bruno Boucard
 
Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5SmartnSkilled
 
Ch8 correction exercices (1)
Ch8 correction exercices (1)Ch8 correction exercices (1)
Ch8 correction exercices (1)abdellah12
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationECAM Brussels Engineering School
 
Javascript : fondamentaux et OOP
Javascript : fondamentaux et OOPJavascript : fondamentaux et OOP
Javascript : fondamentaux et OOPJean-Pierre Vincent
 
Introduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomicsIntroduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomicsAurélien Regat-Barrel
 
Synchroniser ses applis simplement avec akeneo/batch
Synchroniser ses applis simplement avec akeneo/batchSynchroniser ses applis simplement avec akeneo/batch
Synchroniser ses applis simplement avec akeneo/batchgplanchat
 
programmation réseau en java
programmation réseau en java programmation réseau en java
programmation réseau en java Ezéquiel Tsagué
 

Similaire à 17-Concurrence-Fork-Join.pdf (20)

threads.pdf
threads.pdfthreads.pdf
threads.pdf
 
Javascript les générateurs (generators)
Javascript   les générateurs (generators)Javascript   les générateurs (generators)
Javascript les générateurs (generators)
 
Développement informatique : Programmation concurrente
Développement informatique : Programmation concurrenteDéveloppement informatique : Programmation concurrente
Développement informatique : Programmation concurrente
 
ALF 11 - Diagramme de flux de contrôle et WebAssembly
ALF 11 - Diagramme de flux de contrôle et WebAssemblyALF 11 - Diagramme de flux de contrôle et WebAssembly
ALF 11 - Diagramme de flux de contrôle et WebAssembly
 
Les Threads.ppt
Les Threads.pptLes Threads.ppt
Les Threads.ppt
 
Java 7 - Fork/Join
Java 7 - Fork/JoinJava 7 - Fork/Join
Java 7 - Fork/Join
 
Threads
ThreadsThreads
Threads
 
Python + ansible = ♥
Python + ansible = ♥Python + ansible = ♥
Python + ansible = ♥
 
ALT.NET Modéliser Parallèle avec C# 4.0
ALT.NET Modéliser Parallèle avec C# 4.0ALT.NET Modéliser Parallèle avec C# 4.0
ALT.NET Modéliser Parallèle avec C# 4.0
 
Multithreading
MultithreadingMultithreading
Multithreading
 
C++ 11/14
C++ 11/14C++ 11/14
C++ 11/14
 
Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5Support tutoriel : Créer votre jeu en HTML5
Support tutoriel : Créer votre jeu en HTML5
 
Ch8 correction exercices (1)
Ch8 correction exercices (1)Ch8 correction exercices (1)
Ch8 correction exercices (1)
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulation
 
Corrige tp java
Corrige tp javaCorrige tp java
Corrige tp java
 
Javascript : fondamentaux et OOP
Javascript : fondamentaux et OOPJavascript : fondamentaux et OOP
Javascript : fondamentaux et OOP
 
Introduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomicsIntroduction au lock-free programming avec std::atomics
Introduction au lock-free programming avec std::atomics
 
Synchroniser ses applis simplement avec akeneo/batch
Synchroniser ses applis simplement avec akeneo/batchSynchroniser ses applis simplement avec akeneo/batch
Synchroniser ses applis simplement avec akeneo/batch
 
Reseau
ReseauReseau
Reseau
 
programmation réseau en java
programmation réseau en java programmation réseau en java
programmation réseau en java
 

Plus de Patiento Del Mar

Plus de Patiento Del Mar (20)

Managed Extenibility Framework in C# - MEF
Managed Extenibility Framework  in C#  - MEFManaged Extenibility Framework  in C#  - MEF
Managed Extenibility Framework in C# - MEF
 
grpc_tutorial.pdf
grpc_tutorial.pdfgrpc_tutorial.pdf
grpc_tutorial.pdf
 
0.coursGitEclipse.pdf
0.coursGitEclipse.pdf0.coursGitEclipse.pdf
0.coursGitEclipse.pdf
 
Primefaces.ppt
Primefaces.pptPrimefaces.ppt
Primefaces.ppt
 
hibernateormoverview-140501154227-phpapp02.pdf
hibernateormoverview-140501154227-phpapp02.pdfhibernateormoverview-140501154227-phpapp02.pdf
hibernateormoverview-140501154227-phpapp02.pdf
 
hibernateormfeatures-140223193044-phpapp02.pdf
hibernateormfeatures-140223193044-phpapp02.pdfhibernateormfeatures-140223193044-phpapp02.pdf
hibernateormfeatures-140223193044-phpapp02.pdf
 
Spring Data Advanced Querying.ppt
Spring Data Advanced Querying.pptSpring Data Advanced Querying.ppt
Spring Data Advanced Querying.ppt
 
Thymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.pptThymeleaf and Spring Controllers.ppt
Thymeleaf and Spring Controllers.ppt
 
Spring Security.ppt
Spring Security.pptSpring Security.ppt
Spring Security.ppt
 
momjms.pdf
momjms.pdfmomjms.pdf
momjms.pdf
 
rmi.pdf
rmi.pdfrmi.pdf
rmi.pdf
 
synchronization.pdf
synchronization.pdfsynchronization.pdf
synchronization.pdf
 
cours6.pdf
cours6.pdfcours6.pdf
cours6.pdf
 
java_PAR.pdf
java_PAR.pdfjava_PAR.pdf
java_PAR.pdf
 
22-reflection.pdf
22-reflection.pdf22-reflection.pdf
22-reflection.pdf
 
13-Concurrence-Producteur-consommateur.pdf
13-Concurrence-Producteur-consommateur.pdf13-Concurrence-Producteur-consommateur.pdf
13-Concurrence-Producteur-consommateur.pdf
 
15-Concurrence-Operations-Atomiques.pdf
15-Concurrence-Operations-Atomiques.pdf15-Concurrence-Operations-Atomiques.pdf
15-Concurrence-Operations-Atomiques.pdf
 
Concurrence-Interruption-Exceptions.pdf
Concurrence-Interruption-Exceptions.pdfConcurrence-Interruption-Exceptions.pdf
Concurrence-Interruption-Exceptions.pdf
 
Chap7_JavaNet.pdf
Chap7_JavaNet.pdfChap7_JavaNet.pdf
Chap7_JavaNet.pdf
 
module_I6_Sockets.pdf
module_I6_Sockets.pdfmodule_I6_Sockets.pdf
module_I6_Sockets.pdf
 

17-Concurrence-Fork-Join.pdf

  • 2. Vocabulaire Concurrence – Exécuter plusieurs calculs en même temps Parallèle – Un calcul est décomposé et exécuté sur plusieurs threads Vectorisé – Une opération simple (+, min, etc) est exécutée par un même thread sur plusieurs données adajcences en mémoire (sur 128 bits, 256 bits, etc) Distribué – Un calcul est décomposé et exécuté sur plusieurs machines
  • 3. Start / Join Historiquement, le modèle utilisé est start / join Exemple, faire *2 aux valeurs d’un tableau avec 5 threads var threads = IntStream.range(0, 5) .mapToObj(i -> new Thread(() -> { var startIndex = ... var endIndex = ... for(var i = startIndex; i < endIndex; i++) { array[i] *= 2; } })) .toList(); for(var thread: threads) { thread.start(); } for(var thread: threads) { thread.join(); } Recopié du modèle fork() / waitpid() sous Unix
  • 4. Le modèle start / join Avec un tableau de 48 cases [0-9[ [9-18[ [18-28[ [28-38[ [38-48[ start start start start start join join join join join 0 48
  • 5. Problèmes du modèle start / join Le modèle start / join marche bien – si toutes les données sont connus en amont (upfront) Marche bien pour un tableau, moins bien pour un arbre rouge/noir car il faut tout parcourir pour voir toutes les données – si le calcul est homogène en temps Si le calcul est long seulement pour certain index, une thread peut être encore entrain de faire des calculs alors que les autres ont finis => problème de répartition de la charge de calcul
  • 6. Divide and Conquer Solution naive “divide and conquer”, évite d’avoir à connaitre tout les données en amont process(int startIndex, int endIndex) if endIndex – startIndex < SMALL // for(...) return var middle = (startIndex + endIndex) >>> 1; start process(startIndex, middle); start process(middle, endIndex); join join }
  • 7. Fork / Join Evolution de la solution naive “divide and conquer”, on évite d’avoir des threads en attentes process(int startIndex, int endIndex if endIndex – startIndex < SMALL // for(...) return ... var middle = (startIndex + endIndex) >>> 1; fork process(startIndex, middle); var result2 = process(middle, endIndex); var result1 = join return combine result1 result2 }
  • 8. Le modèle fork / join Avec un tableau de 48 cases [0-24[ [24-48[ 0 48 [0-12[ [12-24[ [24-36[ [36-48[ [0-6[ [6-12[ join join join join
  • 9. Problème du modèle fork / join On a toujours le problème de la répartition de la charge de calcul si le calcul est pas homogène en temps Solution: découpler la partie de calcul effectuer de la thread qui effectue le calcul => pool de thread + work stealing
  • 10. Thread Pool avec Work Stealing ● Le nombre de threads est fixe ● Chaque thread à sa queue de tâche ● Si un thread a fini toutes les tâches de sa queue (si la queue est vide), il va voler (steal) des tâches à la fin dans les queues des autres threads => Le fait d’aller voler des tâches permet d’équilibrer la charge de travail
  • 11. fork / join + work stealing Avec un tableau de 48 cases [0-24[ [24-48[ 0 48 [0-12[ [12-24[ [24-36[ [36-48[ [0-6[ [6-12[ [12-18[ [18-24[ [24-30[ [30-36[ [36-42[ [42-48[ steal submit submit submit submit submit submit submit submit
  • 12. Régler fork / join Sélection de SMALL – Si SMALL trop gros, pas assez de tâches, pas assez de parallèlisme – Si SMALL trop petit, trop de tâches, trop d’allocations + queue trop grande process(int startIndex, int endIndex if endIndex – startIndex < SMALL // for(...) return ... ... }
  • 13. Régler fork / join (2) Combine doit être une opération associative On ne peut pas décomposer le calcul si combine(combine(a, b), c) != combine (a, combine(b, c)) process(int startIndex, int endIndex ... var middle = (startIndex + endIndex) >>> 1; fork process(startIndex, middle); var result2 = process(middle, endIndex); var result1 = join return combine result1 result2 } La moyenne (a, b) → (a + b) / 2 est pas associative !
  • 14. Fork / Join en Java
  • 15. java.util.concurrent.ForkJoinPool Le pool de threads (l’ExecutorService) qui fait du work stealing s’appel ForkJoinPool Pour créer un ForkJoinPool – new ForkJoinPool(5) // avec 5 threads Il existe déjà un ForkJoinPool par défaut, – ForkJoinPool.commonPool()
  • 16. Tâches du ForkJoinPool Le ForkJoinPool exécute des ForkJoinTask – RecursiveAction pour les tâches sans valeur – RecursiveTask<V> pour les tâches avec une valeur Exécuter la tâche initiale sur le ForkJoinPool – execute(RecursiveAction) ● L’exécution est asynchrone – invoke(RecursiveAction|RecursiveTask) ● Bloque la thread courante et attend l’exécution de la tâche – submit(RecursiveAction|RecursiveTask) → Future ● Renvoie un Future (Future.get() permet d’avoir la valeur)
  • 17. Rappel sur la suite de Fibonacci Calcul de la suite de Fibonacci en récursif (stupide mais simple) int fibonacci(int n) if (n <= 1) { return n; } return fibonacci(n – 1) + fibonacci(n – 2); } Idée: on peut utiliser le pattern Fork / Join pour faire le calcul en parallèle
  • 18. Exemple de RecursiveTask Calcul de Fibonacci en récursif (stupide mais simple) private static class Fibonacci extends RecursiveTask<Integer> { private final int n; private Fibonacci(int n) { this.n = n; } @Override protected Integer compute() { if (n <= 1) { return 1; } var fib1 = new Fibonacci(n - 1); var fib2 = new Fibonacci(n - 2); f1.fork(); // soumet au ForkJoinPool var result2 = fib2.compute(); // calcul dans la thread courante var result1 = f1.join(); // attend le résultat de la tâche return result1 + result2; // le + est bien associatif } }
  • 19. Exemple de RecursiveTask (2) Calcul de Fibonacci en récursif (stupide mais simple) private static class Fibonacci extends RecursiveTask<Integer> { private final int n; private Fibonacci(int n) { this.n = n; } @Override protected Integer compute() { … } } … var pool = ForkJoinPool.commonPool(); var fibo = new Fibonacci(15); var result = pool.invoke(fibo); // on attend la fin du calcul
  • 20. Exemple de RecursiveAction Multiplier par 2 les valeurs du tableaux private static class MultiplyBy2 extends RecursiveAction { private final int[] array; private final int start; private final int end; private MultiplyBy2(...) { this.array = array; this.start = start; this.end = end; } @Override protected void compute() { if (end – start < 1024) { for(var i = start; I < end; i++) { array[i] *= 2; } return; } var middle = (start + end) >>> 1; var mul1 = new MultiplyBy2(array, start, middle); var mul2 = new MultiplyBy2(array, middle, end); mul2.fork(); mul1.compute(); mul2.join(); } }
  • 21. Exemple de RecursiveAction (2) Multiplier par 2 les valeurs du tableaux private static class MultiplyBy2 extends RecursiveAction { private final int[] array; private final int start; private final int end; private MultiplyBy2(...) { this.array = array; this.start = start; this.end = end; } @Override protected void compute() { ... } } … var pool = ForkJoinPool.commonPool(); var fibo = new MultiplyBy2(array, 0, array.length); var future = pool.submit(fibo); // on récupère un future … System.out.println(future.get()); // on récupère la valeur
  • 22. Relation avec les Streams Lorsque l’on utilise un Stream parallèle stream.parallel(). … le calcul est fait dans le ForkJoinPool par défaut ForkJoinPool.commonPool() L’implantation utilise la méthode Spliterator.trySplit() du Spliterator du Stream pour faire la décomposition récursive