Pourquoi développer des
algorithmes parallèles ?
Pourquoi développer des
algorithmes parallèles ?

Pourquoi développer des
applications multithread ?
Pourquoi développer des
algorithmes parallèles ?

=
Pourquoi développer des
applications multithread ?
Pour exploiter la puissance
des processeurs modernes.
Depuis 2005 bla bla bla
Depuis 2005 bla bla bla
Conséquence :
Il va falloir faire preuve
d’intelligence
pour écrire des applications
performantes !
Depuis 2005
Pour développer des applications perfomantes,
il faut :
Depuis 2005
Pour développer des applications perfomantes,
il faut :
1) Ecrire des traitements multithreads

2) Programmer ...
Boite à outils
1) Ecrire des traitements multithreads :
Java.util.concurrent
2) Programmer chaque traitement (algorithme)
...
Boite à outils
Jusqu’en JDK 6 : rien…
Boite à outils
Jusqu’en JDK 6 : rien…
À partir de JDK 7 :
- Fork / Join
- Parallel Arrays : rend le Fork / Join compatible...
Fork / Join
JDK 7 & 6
Que programme-t-on ?
Initialisation du pool
ForkJoinPool pool = new ForkJoinPool() ;

PrimeFactorsFinderRecursiveTask task...
public class PrimeFactorsFinderRecursiveTask
extends RecursiveTask<PrimeFactors> {
private int start, end ;
protected Prim...
public class PrimeFactorsFinderRecursiveTaskSous-tâche
extends RecursiveTask<PrimeFactors> {
envoyée au pool
private int s...
public class PrimeFactorsFinderRecursiveTask
Appel à get()
extends RecursiveTask<PrimeFactors> {
bloquant
private int star...
public class PrimeFactorsFinderRecursiveTask Calcul
extends RecursiveTask<PrimeFactors> {
proprement
private int start, en...
Quelle stratégie de découpage ?
Au niveau du code
// 1ère strategie
if (end - start > MAX_ITERATIONS) { // Trop gros !
int m = (start + end) / 2 ;
PrimeFa...
// 2ème strategie
if (end - start > MAX_ITERATIONS) { // Trop gros !
List<ForkJoinTask<PrimeFactors>> pfsList =
new ArrayL...
Pièges du Fork / Join
La Java doc nous dit :
Computations should avoid
synchronized blocks or methods
Donc si une tâche a ...
Pièges du Fork / Join
La Java doc nous dit :
Computations should avoid
synchronized blocks or methods
Donc si une tâche a ...
Impossible avec le Fork / Join
Parallel Arrays
JDK 6 & 7
Parallel Arrays
Parallel Arrays : JSR 166y, package extra166y
Dispo en Java 6, embarque le Fork / Join
Inconvénient : en J...
Parallel Arrays
Initialisation
ForkJoinPool pool = new ForkJoinPool() ; // package !

ParralelLongArray a = ParralelLongAr...
Parallel Arrays
Map :
Ops.LongOp add2 = new Ops.LongOp() {
@Override
public long op(long l1) {
return l1 + 2 ;
}
} ;
a2 = ...
Parallel Arrays
Filter :
Ops.LongPredicate filter = new Ops.LongPredicate() {
@Override
public boolean op(long l1) {
retur...
Parallel Arrays
Reduce :
Ops.LongReducer reducer = new Ops.LongReducer() {
@Override
public long op(long l1, long l2) {
re...
Parallel Arrays
Idée :
1) On définit des opérations
2) On « pousse » ces opérations vers le
ParallelArrays, qui effectue l...
Bilan pour les JDK 6 & 7
2 outils disponibles :
- Fork / Join, dans le JDK
- Les parallel arrays, API séparée
Pourquoi l’API Parallel Arrays
n’est-elle pas dans le JDK ?
Bicoze, dans le JDK 8,
on a les …
Et avec les lambdas…
… des nouveaux patterns arrivent !
Et avec les lambdas…
Map :
List<Personne> personnes = new ArrayList<>() ;

personnes.stream()
.map(person -> person.getAge...
Et avec les lambdas…
Filter :
List<Personne> personnes = new ArrayList<>() ;

personnes.stream()
.map(person -> person.get...
Et avec les lambdas…
Reduce :
List<Personne> personnes = new ArrayList<>() ;
int somme =
personnes.stream()
.map(person ->...
Et avec les lambdas…
Et si on le veut en parallèle :
List<Personne> personnes = new ArrayList<>() ;
int somme =
personnes....
API Stream & Collector
Arrivée de deux API : Stream et Collector
Permettent tous les calculs imaginables sur
les collectio...
Exemple : tri d’une liste
Stream<String> stream =
Stream.generate(
() ->
Long.toHexString(ThreadLocalRandom.current().next...
Exemple : tri d’une liste
Stream<String> stream =
Stream.generate(
() ->
Long.toHexString(ThreadLocalRandom.current().next...
Fork / Join, Lambdas

Comment utiliser ces outils efficacement ?
Utilisation
Malheureusement, il ne suffit pas d’appeler
parallel() pour que ça marche …
Problèmes
1) Écrire des réductions « correctes »
2) Régler « correctement » l’algorithmique
3) Utiliser des algorithmes « ...
1er problème : associativité
Une réduction doit être associative
red(a, red(b, c)) = red(red(a, b), c)
1er problème : associativité
Une réduction doit être associative
red(a, red(b, c)) = red(red(a, b), c)
a + (b + c) = (a + ...
1er problème : associativité
Une réduction doit être associative
red(a, red(b, c)) = red(red(a, b), c)
a + (b + c) = (a + ...
1er problème : associativité
Une réduction doit être associative
red(a, red(b, c)) = red(red(a, b), c)
a + (b + c) = (a + ...
2ème problème : performance
Le calcul parallèle est-il vraiment plus rapide ?
• Exemple : tri d’une liste de long
long beg...
Tri en parallèle
Déroulement :
• Division de la liste en N sous-listes
• Tri de chaque liste sur un cœur : QuickSort
• Fus...
Performances
2 stratégies de découpage :
• Boucle for
• Dyadique
• Tri avec Collections.sort
CPU Intel core i7, 8 cœurs
Performances
2 tableaux : 16M et 32M de long

4M

2M
1M

4M
2M
1M
Performances
1) La bonne taille du paquet

4M

13

2M

12

1M

20

4M
2M
1M
Performances
1) La bonne taille du paquet …
dépend de la taille du tableau !
4M

13

2M

12

1M

20

4M

33

2M

54

1M

9...
Performances
2) La stratégie de division est importante !

4M

13

18

2M

12

20

1M

20

35

4M

33

2M

54

1M

93
Performances
2) La stratégie de division est importante !
et dépend de la taille du tableau !
4M

13

18

2M

12

20

1M

...
MAIS…
4M

13

18

2M

12

20

1M

20

35

4M

33

47

2M

54

125

1M

93

211
Quelles sont les performances
sans parallélisation ?

4M

13

18

2M

12

20

1M

20

35

4M

33

47

2M

54

125

1M

93
...
16M

18

32M

36

4M

13

18

2M

12

20

1M

20

35

4M

33

47

2M

54

125

1M

93

211
Écrire du code parallèle
est complexe et coûteux
mais n’amène pas toujours
des gains en performance !
3ème problème
Une anecdote
• Le 24/2/2012, Heinz Kabutz défie
les lecteurs de son (excellent) blog Java
Specialists :
• Ca...
3ème problème
Nombre de Fibonacci
• F0 = 0, F1 = 1
• Fn = Fn – 1 + Fn – 2
F1_000_000 est un nombre trrrès grand !
3ème problème
Record à battre : 5800s (ou 2500 ?) sur un Core
i7 à 8 cœurs
3ème problème
Record à battre : 5800s (ou 2500 ?) sur un Core
i7 à 8 cœurs
1ère amélioration : 3300s sur 4 cœurs
3ème problème
Record à battre : 5800s (ou 2500 ?) sur un Core
i7 à 8 cœurs
1ère amélioration : 3300s sur 4 cœurs

2ème amé...
Où est le miracle ?
Pas de miracle…
- Utilisation de la librairie GNU GMP
- Utilisation d’une bonne implémentation de
BigI...
Où est le miracle ?
Pas de miracle…
- Utilisation de la librairie GNU GMP
- Utilisation d’une bonne implémentation de
BigI...
Algorithmes
Entre 1990 et 2005, l’optimisation linéaire a
gagné un facteur 43M
- 1k est du à la rapidité des processeurs
-...
Avant de paralléliser
mieux vaut s’assurer
que l’on utilise
le bon algorithme
4ème problème
Étude de cas : le voyageur de commerce
Comment résoudre ce problème ?
4ème problème
Étude de cas : le voyageur de commerce
Comment résoudre ce problème
Pas d’algorithme direct, il nous faut un...
Comment paralléliser ?
Problème de tableau, donc facile !
• On coupe le tableau en morceaux
• On déroule sur chaque morcea...
Sauf que …
Ca ne marche pas !
• Les propriétés de convergence sont perdues
en parallèle
Sauf que …
Ca ne marche pas !
• Les propriétés de convergence sont perdues
en parallèle
Le code compile
Il s’exécute
Le ré...
Conclusion
Conclusion
• 1975 : le langage C permet de ne plus gérer la
pile à la main
Conclusion
• 1975 : le langage C permet de ne plus gérer la
pile à la main
• 1995 : Java permet de ne plus gérer la
mémoir...
Conclusion
• 1975 : le langage C permet de ne plus gérer la
pile à la main
• 1995 : Java permet de ne plus gérer la
mémoir...
Conclusion
Aujourd’hui le CPU lui-même devient une
ressource parmi d’autres, au même titre que la
mémoire
Les applications...
Conclusion
Oui la parallélisation devient facile, comme elle
ne l’a jamais été
• On appele parallel(), et en voiture !
Conclusion
Oui la parallélisation devient facile, comme elle
ne l’a jamais été
• On appele parallel(), et en voiture !
Mai...
Conclusion
Oui la parallélisation devient facile, comme elle
ne l’a jamais été
• On appele parallel(), et en voiture !
Mai...
Conclusion
Il y a quelques années on disait :
« un bon programmeur doit apprendre un
nouveau langage tous les N mois / ann...
Conclusion
Aujourd’hui mon conseil :
Conclusion
Aujourd’hui mon conseil :
• 1er mois : apprendre le fonctionnement
interne des CPU
Conclusion
Aujourd’hui mon conseil :
• 1er mois : apprendre le fonctionnement
interne des CPU
• 2ème mois : apprendre à pr...
Conclusion
Aujourd’hui mon conseil :
• 1er mois : apprendre le fonctionnement
interne des CPU
• 2ème mois : apprendre à pr...
Conclusion
La parallélisation des traitements / algorithmes
amène
- De nouvelles opportunités
- Des nouveaux problèmes
- D...
Conclusion
La parallélisation des traitements / algorithmes
amène
- De nouvelles opportunités
- Des nouveaux problèmes
- D...
Merci !
Q/R
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013
Prochain SlideShare
Chargement dans…5
×

Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013

1 226 vues

Publié le

Ces nouvelles fonctionnalités introduites à partir de Java 7 nous permettent de parallèliser nos traitements simplement, voire gratuitement. Nous allons donc pouvoir utiliser pleinement nos multicoeurs avec un minimum d'efforts. Quels sont ces nouveaux patterns, quels gains en performance pouvons-nous en attendre, à quels nouveaux bugs serons-nous confrontés ? Une heure pour répondront à chacun de ces points, en introduisant les nouveaux problèmes et leurs solutions. Une heure pour comprendre comment nos habitudes de programmation vont devoir évoluer, et à quoi la programmation parallèle en Java ressemblera-t-elle demain.

0 commentaire
1 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
1 226
Sur SlideShare
0
Issues des intégrations
0
Intégrations
2
Actions
Partages
0
Téléchargements
17
Commentaires
0
J’aime
1
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Fork / Join, Parallel Arrays, Lambdas : la programmation parallèle (trop ?) facile ! - José paumard Codeurs en seine 2013

  1. 1. Pourquoi développer des algorithmes parallèles ?
  2. 2. Pourquoi développer des algorithmes parallèles ? Pourquoi développer des applications multithread ?
  3. 3. Pourquoi développer des algorithmes parallèles ? = Pourquoi développer des applications multithread ?
  4. 4. Pour exploiter la puissance des processeurs modernes.
  5. 5. Depuis 2005 bla bla bla
  6. 6. Depuis 2005 bla bla bla Conséquence :
  7. 7. Il va falloir faire preuve d’intelligence pour écrire des applications performantes !
  8. 8. Depuis 2005 Pour développer des applications perfomantes, il faut :
  9. 9. Depuis 2005 Pour développer des applications perfomantes, il faut : 1) Ecrire des traitements multithreads 2) Programmer chaque traitement (algorithme) en parallèle
  10. 10. Boite à outils 1) Ecrire des traitements multithreads : Java.util.concurrent 2) Programmer chaque traitement (algorithme) en parallèle ???
  11. 11. Boite à outils Jusqu’en JDK 6 : rien…
  12. 12. Boite à outils Jusqu’en JDK 6 : rien… À partir de JDK 7 : - Fork / Join - Parallel Arrays : rend le Fork / Join compatible JDK 6 À partir de JDK 8 : les lambdas !
  13. 13. Fork / Join JDK 7 & 6
  14. 14. Que programme-t-on ? Initialisation du pool ForkJoinPool pool = new ForkJoinPool() ; PrimeFactorsFinderRecursiveTask task = new PrimeFactorsFinderRecursiveTask(1, 4000) ; ForkJoinTask<PrimeFactors> pfsTask = pool.submit(task) ; PrimeFactors pfs = pfsTask.get() ; // Eq join
  15. 15. public class PrimeFactorsFinderRecursiveTask extends RecursiveTask<PrimeFactors> { private int start, end ; protected PrimeFactors compute() { PrimeFactors pfs = new PrimeFactors() ; if (end - start > MAX_ITERATIONS) { // I’m too big // processing ForkJoinTask<PrimeFactors> task = ... ; task.fork() ; PrimeFactors pfs = task.get() ; ... } else { ... } return pfs ; } }
  16. 16. public class PrimeFactorsFinderRecursiveTaskSous-tâche extends RecursiveTask<PrimeFactors> { envoyée au pool private int start, end ; protected PrimeFactors compute() { PrimeFactors pfs = new PrimeFactors() ; if (end - start > MAX_ITERATIONS) { // I’m too big // processing ForkJoinTask<PrimeFactors> task = ... ; task.fork() ; PrimeFactors pfs = task.get() ; ... } else { ... } return pfs ; } }
  17. 17. public class PrimeFactorsFinderRecursiveTask Appel à get() extends RecursiveTask<PrimeFactors> { bloquant private int start, end ; protected PrimeFactors compute() { PrimeFactors pfs = new PrimeFactors() ; if (end - start > MAX_ITERATIONS) { // I’m too big // processing ForkJoinTask<PrimeFactors> task = ... ; task.fork() ; PrimeFactors pfs = task.get() ; ... } else { ... } return pfs ; } }
  18. 18. public class PrimeFactorsFinderRecursiveTask Calcul extends RecursiveTask<PrimeFactors> { proprement private int start, end ; dit protected PrimeFactors compute() { PrimeFactors pfs = new PrimeFactors() ; if (end - start > MAX_ITERATIONS) { // I’m too big // processing ForkJoinTask<PrimeFactors> task = ... ; task.fork() ; PrimeFactors pfs = task.get() ; ... } else { ... } return pfs ; } }
  19. 19. Quelle stratégie de découpage ?
  20. 20. Au niveau du code // 1ère strategie if (end - start > MAX_ITERATIONS) { // Trop gros ! int m = (start + end) / 2 ; PrimeFactorsFinderTask task1 = new PrimeFactorsFinderTask(start, m) ; PrimeFactorsFinderTask task2 = new PrimeFactorsFinderTask(m, end) ; task1.fork() ; task2.fork() ; PrimeFactors pfs1 = task1.join() ; PrimeFactors pfs2 = task2.join() ; pfs.add(pfs1) ; pfs.add(pfs2) ; }
  21. 21. // 2ème strategie if (end - start > MAX_ITERATIONS) { // Trop gros ! List<ForkJoinTask<PrimeFactors>> pfsList = new ArrayList<ForkJoinTask<PrimeFactors>>() ; for (int i = start ; i < end – MAX_ITERATIONS ; i += MAX_ITERATIONS) { PrimeFactorsFinderRecursiveTask task = new PrimeFactorsFinderRecursiveTask( i, i + MAX_ITERATIONS) ; task.fork() ; pfsList.add(task) ; } for (ForkJoinTask<PrimeFactors> task : pfsList) { PrimeFactors pfsElement = task.join() ; pfs.add(pfsElement) ; } } 30% plus rapide…
  22. 22. Pièges du Fork / Join La Java doc nous dit : Computations should avoid synchronized blocks or methods Donc si une tâche a un état, cet état doit être transmis aux sous-tâches par copie
  23. 23. Pièges du Fork / Join La Java doc nous dit : Computations should avoid synchronized blocks or methods Donc si une tâche a un état, cet état doit être transmis aux sous-tâches par copie Quelles conséquences sur les tableaux ?
  24. 24. Impossible avec le Fork / Join
  25. 25. Parallel Arrays JDK 6 & 7
  26. 26. Parallel Arrays Parallel Arrays : JSR 166y, package extra166y Dispo en Java 6, embarque le Fork / Join Inconvénient : en Java 7 on a 2 classes ForkJoinPool, dans deux packages différents Permet le calcul sur des tableaux de nombres (long ou double) http://g.oswego.edu/dl/concurrency-interest/
  27. 27. Parallel Arrays Initialisation ForkJoinPool pool = new ForkJoinPool() ; // package ! ParralelLongArray a = ParralelLongArray.create(pool) ;
  28. 28. Parallel Arrays Map : Ops.LongOp add2 = new Ops.LongOp() { @Override public long op(long l1) { return l1 + 2 ; } } ; a2 = a.withMapping(add2) ;
  29. 29. Parallel Arrays Filter : Ops.LongPredicate filter = new Ops.LongPredicate() { @Override public boolean op(long l1) { return l1 > 50 ; } } a2 = a.withFilter(filter) ; a2.all() ;
  30. 30. Parallel Arrays Reduce : Ops.LongReducer reducer = new Ops.LongReducer() { @Override public long op(long l1, long l2) { return l1 + l2 ; } } long reducedValue = a.reduce(reducer, 0L) ;
  31. 31. Parallel Arrays Idée : 1) On définit des opérations 2) On « pousse » ces opérations vers le ParallelArrays, qui effectue le calcul, en parallèle ! Approche qui ressemble à la programmation fonctionnelle
  32. 32. Bilan pour les JDK 6 & 7 2 outils disponibles : - Fork / Join, dans le JDK - Les parallel arrays, API séparée
  33. 33. Pourquoi l’API Parallel Arrays n’est-elle pas dans le JDK ?
  34. 34. Bicoze, dans le JDK 8, on a les …
  35. 35. Et avec les lambdas… … des nouveaux patterns arrivent !
  36. 36. Et avec les lambdas… Map : List<Personne> personnes = new ArrayList<>() ; personnes.stream() .map(person -> person.getAge())
  37. 37. Et avec les lambdas… Filter : List<Personne> personnes = new ArrayList<>() ; personnes.stream() .map(person -> person.getAge()) .filter(age -> age > 20)
  38. 38. Et avec les lambdas… Reduce : List<Personne> personnes = new ArrayList<>() ; int somme = personnes.stream() .map(person -> person.getAge()) .filter(age -> age > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  39. 39. Et avec les lambdas… Et si on le veut en parallèle : List<Personne> personnes = new ArrayList<>() ; int somme = personnes.parallelStream() .map(person -> person.getAge()) .filter(age -> age > 20) .reduce(0, (a1, a2) -> a1 + a2) ;
  40. 40. API Stream & Collector Arrivée de deux API : Stream et Collector Permettent tous les calculs imaginables sur les collections de grande taille… En parallèle !
  41. 41. Exemple : tri d’une liste Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ; Stream stream = stream1 .map(s -> s.substring(0, 10)) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ; Exécution : 14 s
  42. 42. Exemple : tri d’une liste Stream<String> stream = Stream.generate( () -> Long.toHexString(ThreadLocalRandom.current().nextLong()) ) ; Stream stream = stream1.parallel() .map(s -> s.substring(0, 10)) .limit(10_000_000) .sorted() ; Object [] sorted = stream.toArray() ; Exécution : 14 s En parallèle : 3,5 s
  43. 43. Fork / Join, Lambdas Comment utiliser ces outils efficacement ?
  44. 44. Utilisation Malheureusement, il ne suffit pas d’appeler parallel() pour que ça marche …
  45. 45. Problèmes 1) Écrire des réductions « correctes » 2) Régler « correctement » l’algorithmique 3) Utiliser des algorithmes « corrects »
  46. 46. 1er problème : associativité Une réduction doit être associative red(a, red(b, c)) = red(red(a, b), c)
  47. 47. 1er problème : associativité Une réduction doit être associative red(a, red(b, c)) = red(red(a, b), c) a + (b + c) = (a + b) + c Correct
  48. 48. 1er problème : associativité Une réduction doit être associative red(a, red(b, c)) = red(red(a, b), c) a + (b + c) = (a + b) + c Correct (a + (b + c)2)2 = ((a + b) 2 + c)2 Oops !
  49. 49. 1er problème : associativité Une réduction doit être associative red(a, red(b, c)) = red(red(a, b), c) a + (b + c) = (a + b) + c Correct (a + (b + c)2)2 = ((a + b) 2 + c)2 Oops ! Le compilateur ou la JVM vont-ils m’aider ?
  50. 50. 2ème problème : performance Le calcul parallèle est-il vraiment plus rapide ? • Exemple : tri d’une liste de long long begin = System.nanoTime() ; List<String> hugeList = new ArrayList<>(N) ; // N = 16_000_000 for (int i = 0 ; i < N ; i++) { long l = random.nextLong() ; l = l > 0 ? l : -l ; hugeList.add(Long.toString(l, 32)) ; }
  51. 51. Tri en parallèle Déroulement : • Division de la liste en N sous-listes • Tri de chaque liste sur un cœur : QuickSort • Fusion des listes triés : MergeSort Coût : calcul + déplacement des données de cœur en cœur
  52. 52. Performances 2 stratégies de découpage : • Boucle for • Dyadique • Tri avec Collections.sort CPU Intel core i7, 8 cœurs
  53. 53. Performances 2 tableaux : 16M et 32M de long 4M 2M 1M 4M 2M 1M
  54. 54. Performances 1) La bonne taille du paquet 4M 13 2M 12 1M 20 4M 2M 1M
  55. 55. Performances 1) La bonne taille du paquet … dépend de la taille du tableau ! 4M 13 2M 12 1M 20 4M 33 2M 54 1M 93
  56. 56. Performances 2) La stratégie de division est importante ! 4M 13 18 2M 12 20 1M 20 35 4M 33 2M 54 1M 93
  57. 57. Performances 2) La stratégie de division est importante ! et dépend de la taille du tableau ! 4M 13 18 2M 12 20 1M 20 35 4M 33 47 2M 54 125 1M 93 211
  58. 58. MAIS… 4M 13 18 2M 12 20 1M 20 35 4M 33 47 2M 54 125 1M 93 211
  59. 59. Quelles sont les performances sans parallélisation ? 4M 13 18 2M 12 20 1M 20 35 4M 33 47 2M 54 125 1M 93 211
  60. 60. 16M 18 32M 36 4M 13 18 2M 12 20 1M 20 35 4M 33 47 2M 54 125 1M 93 211
  61. 61. Écrire du code parallèle est complexe et coûteux mais n’amène pas toujours des gains en performance !
  62. 62. 3ème problème Une anecdote • Le 24/2/2012, Heinz Kabutz défie les lecteurs de son (excellent) blog Java Specialists : • Calculer les 10 premiers et 10 derniers chiffres du nombre de Fibonacci d’indice 1 milliard
  63. 63. 3ème problème Nombre de Fibonacci • F0 = 0, F1 = 1 • Fn = Fn – 1 + Fn – 2 F1_000_000 est un nombre trrrès grand !
  64. 64. 3ème problème Record à battre : 5800s (ou 2500 ?) sur un Core i7 à 8 cœurs
  65. 65. 3ème problème Record à battre : 5800s (ou 2500 ?) sur un Core i7 à 8 cœurs 1ère amélioration : 3300s sur 4 cœurs
  66. 66. 3ème problème Record à battre : 5800s (ou 2500 ?) sur un Core i7 à 8 cœurs 1ère amélioration : 3300s sur 4 cœurs 2ème amélioration : 51s sur 1 cœur (pas de parallélisation)
  67. 67. Où est le miracle ? Pas de miracle… - Utilisation de la librairie GNU GMP - Utilisation d’une bonne implémentation de BigInteger - Meilleur algorithme de calcul des nombres de Fibonacci - Suppression de la récursivité
  68. 68. Où est le miracle ? Pas de miracle… - Utilisation de la librairie GNU GMP - Utilisation d’une bonne implémentation de BigInteger - Meilleur algorithme de calcul des nombres de Fibonacci - Suppression de la récursivité Quel rapport avec le parallélisme ??
  69. 69. Algorithmes Entre 1990 et 2005, l’optimisation linéaire a gagné un facteur 43M - 1k est du à la rapidité des processeurs - 43k sont dus à celle des algorithmes
  70. 70. Avant de paralléliser mieux vaut s’assurer que l’on utilise le bon algorithme
  71. 71. 4ème problème Étude de cas : le voyageur de commerce Comment résoudre ce problème ?
  72. 72. 4ème problème Étude de cas : le voyageur de commerce Comment résoudre ce problème Pas d’algorithme direct, il nous faut une approche « stochastique » (= avec de l’aléatoire) Et c’est encore un problème de tableaux !
  73. 73. Comment paralléliser ? Problème de tableau, donc facile ! • On coupe le tableau en morceaux • On déroule sur chaque morceau • Et on fusionne
  74. 74. Sauf que … Ca ne marche pas ! • Les propriétés de convergence sont perdues en parallèle
  75. 75. Sauf que … Ca ne marche pas ! • Les propriétés de convergence sont perdues en parallèle Le code compile Il s’exécute Le résultat est faux…
  76. 76. Conclusion
  77. 77. Conclusion • 1975 : le langage C permet de ne plus gérer la pile à la main
  78. 78. Conclusion • 1975 : le langage C permet de ne plus gérer la pile à la main • 1995 : Java permet de ne plus gérer la mémoire à la main
  79. 79. Conclusion • 1975 : le langage C permet de ne plus gérer la pile à la main • 1995 : Java permet de ne plus gérer la mémoire à la main • 2013 : Java (encore lui) permet de ne plus gérer le multicœur à la main !
  80. 80. Conclusion Aujourd’hui le CPU lui-même devient une ressource parmi d’autres, au même titre que la mémoire Les applications vont pouvoir s’exécuter en parallèle de façon transparente
  81. 81. Conclusion Oui la parallélisation devient facile, comme elle ne l’a jamais été • On appele parallel(), et en voiture !
  82. 82. Conclusion Oui la parallélisation devient facile, comme elle ne l’a jamais été • On appele parallel(), et en voiture ! Mais…
  83. 83. Conclusion Oui la parallélisation devient facile, comme elle ne l’a jamais été • On appele parallel(), et en voiture ! Mais… • Mon traitement est-il plus rapide ? • Mes résultats sont-ils corrects ?
  84. 84. Conclusion Il y a quelques années on disait : « un bon programmeur doit apprendre un nouveau langage tous les N mois / années »
  85. 85. Conclusion Aujourd’hui mon conseil :
  86. 86. Conclusion Aujourd’hui mon conseil : • 1er mois : apprendre le fonctionnement interne des CPU
  87. 87. Conclusion Aujourd’hui mon conseil : • 1er mois : apprendre le fonctionnement interne des CPU • 2ème mois : apprendre à programmer les algorithmes complexes
  88. 88. Conclusion Aujourd’hui mon conseil : • 1er mois : apprendre le fonctionnement interne des CPU • 2ème mois : apprendre à programmer les algorithmes complexes • 3ème mois : apprendre la version parallèle de ces algorithmes
  89. 89. Conclusion La parallélisation des traitements / algorithmes amène - De nouvelles opportunités - Des nouveaux problèmes - De nouvelles catégories de bugs
  90. 90. Conclusion La parallélisation des traitements / algorithmes amène - De nouvelles opportunités - Des nouveaux problèmes - De nouvelles catégories de bugs Et donc de nouveaux challenges !
  91. 91. Merci !
  92. 92. Q/R

×