9. 1) De quoi cette puissance est-elle faite ?
Architectures muticœurs
2) Conséquences sur la façon de coder ?
3) Quels sont les problèmes des applications
« parallèles » ?
Synchronisation
4) Comment s’y prendre pour
« synchroniser » les opérations
10. 5) De quels outils dispose-t-on ?
Java 6
Java 7
Java 8
6) Importance de l’algorithmique ?
19. Processeurs multicœurs
• Fin de la « soupe gratuite »
• La puissance de calcul augmente
par le nombre de cœurs, et non
plus par l’augmentation de la fréquence de
chaque cœur
• Apparition des GPGPU (2007)
20. Processeurs multicœurs
• Aujourd’hui
– Smartphone : 2 à 4 cœurs
– PC de bureau : 4 à 8 cœurs
– Serveur pro : 128 cœurs
• Demain
– Plateforme exascale : de 100k à 1M
nœuds
– Chaque nœud : de 1k à 10k cœurs
21. Java.util.concurrent
• API de concurrence avancée
– Dispo en Java 4 EDU.oswego
• Nouvelle approche du multithreading
– Nouvelle façon de lancer des threads
– Nouvelle façon de synchroniser les
opérations
37. Solutions aux « data races »
• Synchronisation de la modification des
variables
« garder les variables »
• Et si on ne modifie jamais nos variables ?
Systèmes immutables
39. Java Memory Model
• Objet : définir la valeur d’une variable à un
instant donné
• Règle : « toute lecture d’une variable
retourne la dernière valeur écrite dans cette
variable »
40. Java Memory Model
• Objet : définir la valeur d’une variable à un
instant donné
• Règle : « toute lecture d’une variable
retourne la dernière valeur écrite dans cette
variable »
• Monothread : contrainte sur le
fonctionnement des compilateurs &
de la JVM
41. Java Memory Model
• Objet : définir la valeur d’une variable à un
instant donné
• Règle : « toute lecture d’une variable
retourne la dernière valeur écrite dans cette
variable »
• Multithread multicœur : vrai problème !
« toute lecture … retourne
la dernière valeur écrite … »
42. Java Memory Model
• Multithread multicœur : vrai problème !
« toute lecture … retourne
la dernière valeur écrite … »
• Chronologie entre les threads / cœurs
43. Happens before
• Chronologie = créer des liens entre les
lectures et les écritures
x = 1
r1 = x
• Valeur de r1 ?
– Même thread ⇒ évident
– Threads différents : impossible de prévoir
44. Happens before
• Chronologie = créer des liens entre les
lectures et les écritures
x = 1
happens before
r1 = x
• Valeur de r1 ?
– Imposée par la JLS : r1 = 1
45. Happens before
• Règle :
Un lien « happens before » est établi
entre toute écriture synchronisée ou
volatile et toute lecture synchronisée ou
volatile qui suit
• Existence de ce lien ⇒
Javadoc (java.util.concurrent)
46. private int index ;
public void incrementonsGaiement() {
index++ ;
}
public void testonsJoyeusement() {
if (index > 10) {
System.out.println("Index est grand !") ;
}
}
47. Pas de lien
« happens before »
private int index ;
public void incrementonsGaiement() {
index++ ;
}
public void testonsJoyeusement() {
if (index > 10) {
System.out.println("Index est grand !") ;
}
}
48. private volatile int index ;
public void incrementonsGaiement() {
index++ ;
}
public void testonsJoyeusement() {
if (index > 10) {
System.out.println("Index est grand !") ;
}
}
49. Lien
« happens before »
private volatile int index ;
public void incrementonsGaiement() {
index++ ;
}
public void testonsJoyeusement() {
if (index > 10) {
System.out.println("Index est grand !") ;
}
}
50. Cas pathologique
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) { r1 = y ;
y = 1 ; }
} r2 = x ;
} }
51. 1) T1 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) {
y = 1 ;
2 }
r1 = y ;
} r2 = x ;
} }
52. 1) T1 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) {
y = 1 ;
2 }
r1 = y ;
} 3 r2 = x ;
} }
4
53. 1) T1 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) {
y = 1 ;
2 }
r1 = y ;
} 3 r2 = x ;
} }
4
r2 = 1
54. 1) T2 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) { r1 = y ;
y = 1 ; }
} r2 = x ;
} }
4
55. 1) T2 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) { r1 = y ;
y = 1 ; ? ? }
} r2 = x ;
} }
4
56. 1) T2 prend le lock
private int x, y, r1, r2 ;
private Object lock = new Object() ;
void maMethode() { 1 void maMethodeAussi() {
x = 1 ; synchronized(lock) {
synchronized(lock) { r1 = y ;
y = 1 ; ? ? }
} r2 = x ;
} }
4
r2 = 0 ? r2 = 1 ?
57. Double
public class Singleton {
check locking
private static Singleton instance ;
Lecture non
public static Singleton synchronisée
getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton() ;
}
}
}
return instance ;
}
}
58. Variable volatile !
public class Singleton {
private volatile static Singleton instance ;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton() ;
}
}
}
return instance ;
}
}
72. Barrière de mémoire
• Une instruction assembleur
• Permet de « propager » une modification
d’un cœur à l’autre
• Invalide le cache L1, force sa relecture dans
L3 ou la mémoire
73.
74.
75.
76.
77. Optimisation 1
Concevoir des traitements tels que :
• Les données tiennent dans 32kO
• Le code tient dans 32kO
• Et pas de barrière de mémoire
⇒ Exécution à 1GHz
78.
79. Optimisation 2
• S’arranger pour placer ses données dans
des zones contigües de la mémoire
⇒ Divise pas 8 les temps de chargement
80. Cadeau empoisonné de L3
• Le cache invalide les données ligne par ligne
• Cas de deux threads :
– Le premier exécute une méthode, accède
àa
– Le second exécute une autre méthode,
accède à b
• Pas de concurrence d’accès sur a et b
81.
82.
83.
84.
85.
86.
87.
88.
89. Cadeau empoisonné de L3
• Catastrophique pour les perf !
• Solution : recours au « variable padding »
• Utilisé dans le disruptor (LMAX)
91. Conclusion
• Prendre en compte le processeur
– Permet de belles optimisations
– Délicat à utiliser !
• Tenir compte de l’ordre des variables en mémoire
92. Conclusion
• Prendre en compte le processeur
– Permet de belles optimisations
– Délicat à utiliser !
• Tenir compte de l’ordre des variables en mémoire
• Veut-on le faire ?
• Le langage est-il prêt ?
95. La manière du JDK 1.1
Runnable r = new Runnable() {
public void run() {
while (true) {
System.out.println("Vogue la galère !") ;
}
}
} ;
Thread t = new Thread(r) ;
t.start() ;
96. La manière du JDK 1.1
Runnable r = new Runnable() {
public void run() {
while (true) {
System.out.println("Vogue la galère !") ;
}
}
} ;
Thread t = new Thread(r) ;
t.start() ;
1) Pas de type de retour
2) Pas d’exception
3) Création / destruction
97. La manière du JDK 5
ExecutorService service =
new ScheduledThreadPoolExecutor(10) ;
Callable<Boolean> task = new Callable<Boolean>() {
public Boolean call() throws Exception {
int i = 0 ;
while (i++ < 1000) {
System.out.println("Vogue la galère !") ;
}
return true ;
} 1) Type de retour, exception
};
2) Cycle de vie géré par le pool
Future<Boolean> f = service.submit(task) ;
Boolean b = f.get(100, TimeUnit.MILLISECONDS) ;
104. Classe CountDownLatch
• Utilisable en initialisation (pas de reset)
private CountDownLatch latch = new CountDownLatch() ;
public void init() {
db.connect() ; // étape « lente »
latch.countDown() ; // ouverture du latch
} ;
public void process() {
latch.await() ; // attente de l’ouverture
} ;
105. Classe CyclicBarrier
• Synchronisation sur la fin du traitement de
plusieurs tâches
private CyclicBarrier barrier = new CyclicBarrier(4) ;
public void processOne() {
// traitements divers
barrier.await() ;
} ;
public void processTwo() {
// traitements divers
barrier.await() ;
} ;
106. Interface ReadWriteLock
• Gère des paires de Lock
– Un pour la lecture
– Un pour l’écriture
• Lecture : concurrence possible
• Écriture : exclusive
• Visibilité des modifications garantie
107. Interface ReadWriteLock
private ReadWriteLock rwLock = new ReentrantReadWriteLock() ;
• Pas de contrainte sur les lectures
private ReadWriteLock rwLock = new ReentrantReadWriteLock() ;
public void maMethodeQuiLit() {
Lock readLock = rwLock.readLock() ;
readLock.lock() ;
// traitements de lecture
readLock.unlock() ;
} ;
108. Interface ReadWriteLock
• Les écritures stoppent les lectures
• Une seule écriture à la fois
private ReadWriteLock rwLock = new ReentrantReadWriteLock() ;
public void maMethodeQuiEcrit() {
Lock writeLock = rwLock.writeLock() ;
writeLock.lock() ;
// traitements de lecture
writeLock.unlock() ;
} ;
110. Types atomiques
• AtomicLong, AtomicFloat, etc…
private AtomicInteger index = new AtomicInteger(0) ;
public void uneMethode() {
long newValue = index.incrementAndGet() ;
}
• Compile en une unique instruction
assembleur
111. Types atomiques
• Principe du CAS « compare and swap »
• Prend en entrée :
– Une adresse mémoire
– Deux valeurs « ancienne » & « nouvelle »
• Si la valeur lue à l’adresse correspond à
l’ancienne alors la nouvelle est recopiée
• Sinon, retourne false
• Pas de synchronisation !
112. Types atomiques
// classe AtomicLong
public final long incrementAndGet() {
for (;;) {
long current = get() ;
long next = current + 1 ;
if (compareAndSet(current, next))
return next ;
}
}
113. Files d’attente
• BlockingQueue
– File d’attente de taille fixe
– add(e), offer(e), put(e) avec timeout
– remove(e), poll(e), take(e)
– element(e), examine(e)
• Thread safe (locks internes)
– Utilisable pour le pattern producteur /
consommateur
114. Tableau immutable
• CopyOnWriteArrayList (et version LinkedList)
– Lecture synchronisée
– Écriture par duplication
• Le tableau lu n’est jamais modifié, donc on
peut le partager sans synchronisation
115.
116.
117.
118.
119.
120. Structures immutables
• Peut-on construire d’autres structures
immutables dans l’API Collection ?
– Tables de hachage
• Réponse : oui, et de façon efficace
– Ce qui n’est pas le cas des tableaux
123. Philisophie
• Base de données :
begin
update ... set ... where ...
commit
• Sur le commit :
– Succès : on continue
– Échec : on annule les modifications
– Règles de visibilité
• Idée de 1986, implémentations 1995
124. Même chose dans la JVM ?
• Plusieurs implémentations
• Akka 1.3.1
<dependency>
<groupId>se.scalablesolutions.akka</groupId>
<artifactId>akka-kernel</artifactId>
<version>1.3.1</version>
</dependency>
<repository>
<id>Akka</id>
<name>Akka Maven2 Repository</name>
<url>http://akka.io/repository/</url>
</repository>
125. Pattern STM Effectué dans
une transaction
final Ref<Integer> source = new Ref<Integer>(500) ;
final Atomic<Object> atom = new Atomic<Object>() {
@Override
public Object atomically() {
source.swap(source.get() + 1) ;
return null ;
}
} ;
atom.execute() ;
126. Pattern STM Exécuté dans le
thread courant
final Ref<Integer> source = new Ref<Integer>(500) ;
final Atomic<Object> atom = new Atomic<Object>() {
@Override
public Object atomically() {
source.swap(source.get() + 1) ;
return null ;
}
} ;
atom.execute() ;
127. Pattern STM
• Une transaction qui échoue est rejouée
(idem AtomicLong)
– Utilisation du processeur différente de la
synchronisation
• Cas applicatifs non accessibles aux Locks
Object o = queue1.remove() ;
queue2.add(o) ;
128. Pattern STM
• Bons cas applicatifs : faible concurrence
d’accès
• Forte concurrence : les cœurs passent leur
temps à rejouer le code
129. Implémentation des STM
• Peut utiliser les Locks
• Peut ne pas les utiliser
Dans ce cas : quid de la visibilité (pas de
barrière de mémoire)
Visibilité : comment l’intégrité des données
est-elle garantie ?
130. Implémentation des STM
• Consomme des ressources (mémoire)
– Fonction du nombre de transactions
– Peut être en O(n2) (très mauvais)
• Concurrence avec les Locks, qui n’en
consomment pas
131. Futur des STM
• Architecture Haswell d’Intel (2013)
– Support de TSX (Transactional
Synchronisation Extension)
– TSX permet de marquer des portions de
code
– La mémoire modifiée n’est visible que du
thread qui modifie cette mémoire
– Elle est publiée « instantanément » sur
commit
133. Modèle d’acteurs
1973
• Un acteur réalise un traitement
• Il reçoit un message immutable
• Effectue un traitement, et retourne
éventuellement un résultat
Non synchronisé car sans accès concurrent
134. Modèle d’acteurs
• Utilisé en Erlang
• Plusieurs API Java et sur la JVM (Scala,
Clojure)
Akka : écrit en Scala
135. Akka : fonctionnement
• Akka gère un pool de threads
• On crée des acteurs, on leur envoie des
messages
• Un message reçu est traité dans un thread
• Un acteur ne peut pas être exécuté dans
deux threads à la fois
• Pas de concurrence d’accès sur son éventuel
état
136. 4000! – Akka
public class PrimeFinderActor extends UntypedActor {
public void onReceive(Object o) throws Exception {
List<Integer> bounds = (List<Integer>)o ;
int debut = bounds.get(0) ;
int fin = bounds.get(1) ;
PrimeFactors pfs = new PrimeFactors() ;
for (int i = debut ; i < fin ; i++) {
PrimeFactors pfi = pfs.getPrimeFactors(i) ;
pfs.add(pfi) ;
} // return pfs ;
getContext().reply(pfs) ;
}
}
137. 4000! - ExecutorService
public class PrimeFactorCallable
implements Callable<PrimeFactors> {
private int debut, fin ;
public PrimeFactorCallable(int debut, int fin) {
this.debut = debut ; this.fin = fin ;
}
public PrimeFactors call() throws Exception {
PrimeFactors pfs = new PrimeFactors() ;
for (int i = debut ; i < fin ; i++) {
PrimeFactors pfi = pfs.getPrimeFactors(i) ;
pfs.add(pfi) ;
}
return pfs ;
}
}
138. public static void main(String[] args) {
Future [] futures = new Future [400] ;
for (int i = 0 ; i < futures.length ; i++) {
List<Integer> bound = Collections.unmodifiableList(
Arrays.asList(10*i, 10*(i + 1))) ;
ActorRef primeFactorFinder =
Actors.actorOf(PrimeFinderActor.class).start() ;
futures[i] =
primeFactorFinder.sendRequestReplyFuture(bound) ;
}
PrimeFactors pfs = new PrimeFactors() ;
for (int i = 0 ; i < futures.length ; i++) {
pfs.add((PrimeFactors)futures[i].get()) ;
}
Actors.registry().shutdownAll() ;
}
139. public static void main(String... args)
throws ExecutionException, InterruptedException {
ExecutorService es = new ScheduledThreadPoolExecutor(10) ;
Future [] futures = new Future [400] ;
for (int i = 0 ; i < futures.length ; i++) {
PrimeFactorCallable callable =
new PrimeFactorCallable(10*i, 10*(i + 1)) ;
futures[i] = es.submit(callable) ;
}
PrimeFactors pfs = new PrimeFactors() ;
for (int i = 0 ; i < futures.length ; i++) {
pfs.add((PrimeFactors)futures[i].get()) ;
}
es.shutdown() ;
}
141. Performances
• Les codes se ressemblent
• Utilisation CPU : idem
• Temps de calcul :
– Akka : 2,7s
– ExecutorService : 2,3s
• Différence non significative
142. Exécution transactionnelle
public class Balance {
public final int montant ;
public Balance(int montant) {
this.montant = montant ;
}
}
public class Retrait {
public final int montant ;
public Retrait(int montant) {
this.montant = montant ;
}
}
143. Exécution transactionnelle
public class Depot {
public final int montant ;
public Depot(int montant) {
this.montant = montant ;
}
}
public class GetBalance {
}
144. Exécuté dans
une transaction
public class CompteBancaire extends UntypedTransactor {
private final Ref<Integer> balance = new Ref<Integer>(0) ;
public void atomically(final Object message) {
if (message instanceof Depot) {
int montant = ((Depot)message).montant ;
if (montant > 0)
balance.swap(balance.get() + montant) ;
}
if (message instanceof Retrait) {
int montant = ((Retrait)message).montant ;
if (montant > 0) {
if (balance.get() < montant)
throw new IllegalStateException("...") ;
balance.swap(balance.get() - montant) ;
}
}
if (message instanceof GetBalance)
getContext().replySafe((new Balance(balance.get()))) ;
}
}
145. Fera échouer la
transaction
public class CompteBancaire extends UntypedTransactor {
private final Ref<Integer> balance = new Ref<Integer>(0) ;
public void atomically(final Object message) {
if (message instanceof Depot) {
int montant = ((Depot)message).montant ;
if (montant > 0)
balance.swap(balance.get() + montant) ;
}
if (message instanceof Retrait) {
int montant = ((Retrait)message).montant ;
if (montant > 0) {
if (balance.get() < montant)
throw new IllegalStateException("...") ;
balance.swap(balance.get() - montant) ;
}
}
if (message instanceof GetBalance)
getContext().replySafe((new Balance(balance.get()))) ;
}
}
146. public class Transfert {
public final ActorRef source ;
public final ActorRef destination ;
public final int montant ;
public Transfert(final ActorRef source,
final ActorRef destination,
final int montant) {
this.source = source ;
this.destination = destination ;
this.montant = montant ;
}
}
147. Ensemble d’appels
d’acteurs
public class ServiceBancaire extends UntypedTransactor {
public Set<SendTo> coordinate(final Object message) {
if (message instanceof Transfert) {
Set<SendTo> s = new HashSet<SendTo>() ;
Transfert t = (Transfert)message ;
if (t.montant > 0) { // validation
s.add(sendTo(t.destination, new Depot(t.montant))) ;
s.add(sendTo(t.source, new Retrait(t.montant))) ;
return Collections.unmodifiableSet(s) ;
}
}
return nobody() ;
}
}
148. Ensemble d’appels
d’acteurs
public class ServiceBancaire extends UntypedTransactor {
public Set<SendTo> coordinate(final Object message) {
if (message instanceof Transfert) {
Set<SendTo> s = new HashSet<SendTo>() ;
Transfert t = (Transfert)message ;
if (t.montant > 0) { // validation
s.add(sendTo(t.destination, new Depot(t.montant))) ;
s.add(sendTo(t.source, new Retrait(t.montant))) ;
return Collections.unmodifiableSet(s) ;
}
}
return nobody() ;
}
}
149. Appel dans une
transaction
public class ServiceBancaire extends UntypedTransactor {
public Set<SendTo> coordinate(final Object message) {
if (message instanceof Transfert) {
Set<SendTo> s = new HashSet<SendTo>() ;
Transfert t = (Transfert)message ;
if (t.montant > 0) { // validation
s.add(sendTo(t.destination, new Depot(t.montant))) ;
s.add(sendTo(t.source, new Retrait(t.montant))) ;
return Collections.unmodifiableSet(s) ;
}
}
return nobody() ;
}
}
150. Exécution transactionnelle
• Chaque acteur gère un compte
• Il possède un état, mais qui n’est jamais
partagé
• Les transferts sont gérés par un acteur sans
état
• Toutes les opérations sont transactionnelles
151. Bilan STM & acteurs
• STM & acteurs : deux approches
alternatives à synchronized
• Offrent des fonctionnalités
supplémentaires
• Vont bénéficier du support de l’assembleur
• Pas de miracle au niveau performance
153. Nouveautés Java 7 & 8
• Pattern Fork / Join
• API non incluse en Java 7 : parallel arrays
• Méthode en Java 8 : parallel()
Objet :
• Automatiser la parallélisation sur les
tableaux et les collections
• Ne plus avoir à écrire de code de
parallélisation
155. Fork / Join
• Traite des calculs sur de grandes quantités
de données, numériques ou non
• Gère des tâches
– Si elle est trop grosse elle se divise : Fork
– Sinon elle s’exécute et retourne son
résultat
• Une tâche qui s’est divisée récupère les
résultats de ses sous-tâches : Join
156. Fork / Join
• Gère un pool de threads
• Chaque thread possède une file d’attente
qui stocke les tâches
• Quand sa file d’attente est vide, il va voler
du travail à son voisin
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171. Fork / Join
• Chaque tâche travaille sur ses propres
données
• Pas de bloc synchronisé
• Pas de variables partagées
• On doit donc « dupliquer » des informations
• Cas du traitement des tableaux de grande
taille ?
180. Parallel Arrays
• API non incluse dans le JDK
• Peut être utilisée en Java 6
• Fork / Join dans un package jsr166y.*
• Avantage : API auto-porteuse
187. Méthodes virtuelles d’ext.
• On ajoute des méthodes dans les interfaces,
avec leurs implémentation par défaut
public interface Collection<E> {
public boolean add(E e) ;
public boolean addAll(Collection<? extends E> c) ;
// la litanie des méthodes de Collection
// méthode virtuelle d'extension
public void sort(Comparator<? super E> comp)
default Collections.sort(comp) ;
}
188. Syntaxe
• Du light !
int maxAge =
persons.map(p -> p.getAge()).reduce(0, Integer::max) ;
Collection<Person> oldies =
persons.filter(p -> p.age > 40).into(new ArrayList()) ;
• Support du parallélisme
Collection<Person> oldies =
persons.parallel().filter(p -> p.age > 40)
.into(new ArrayList()) ;
189. Interface spliterable()
• Extension d’iterable()
• Type de retour de parallel()
• Le fork / join devient un framework système
• Les fonctionnalités des parallel arrays sont
intégrées via les lambdas
• On gagne java.util.parallel
191. Une anecdote
Le 24/2/2012 Heinz Kabutz
lance un défi :
• Déterminer les 10 premiers et
10 derniers octets du nombre
Fibonacci (1 milliard)
• Taille du nombre écrit dans
un fichier = ~180Mo
193. Une anecdote
• Record à battre : 5600s sur un CPU 8 cœurs
• 1ère amélioration : 3300s sur un CPU 4
cœurs
194. Une anecdote
• Record à battre : 5600s sur un CPU 8 cœurs
• 1ère amélioration : 3300s sur un CPU 4
cœurs
• 2ème amélioration : 51s sur un cœur
195. Une anecdote
Par quel miracle ?
• Utilisation de GNU Multiple Precision
Arithmetic Lib (GMP)
• Changement d’implémentation de
BigInteger
• Changement d’algorithme de calcul de
Fibonacci
• Suppression de l’utilisation de la récursivité
196. Par quel miracle ?
Quel rapport avec
• Utilisation de GNU Multiple Precision
Arithmetic Lib (GMP)
• Changement d’implémentation de
Fork / Join ou la
BigInteger
• Changement d’algorithme de calcul de
parallélisation ?
Fibonacci
• Suppression de l’utilisation de la récursivité
197. Algorithmique
L’optimisation des fonctions linéaires a gagné
un facteur 43M sur les temps de traitement
en 15 ans :
• 1k est dû à la vitesse des processeurs
• 43k est dû à l’amélioration des algorithmes
198. Moralité
• Utiliser les bons algorithmes de traitement
avant de penser à la parallélisation
199. Moralité
• Utiliser les bons algorithmes de traitement
avant de penser à la parallélisation
• S’assurer que les algorithmes sont bien
parallélisables !!
200. Moralité
• Utiliser les bons algorithmes de traitement
avant de penser à la parallélisation
• S’assurer que les algorithmes sont bien
parallélisables !!
Quick sort : se parallélise mal
• Merge sort consomme de la mémoire
201. Moralité
• Utiliser les bons algorithmes de traitement
avant de penser à la parallélisation
• S’assurer que les algorithmes sont bien
parallélisables !!
Recuit simulé : ne se parallélise pas, perd ses
propriétés de convergence
203. Conclusion
Une nouvelle période charnière se déroule en
ce moment
En 1995 : la mémoire est devenue une
ressource gérée par la JVM
En 2012 : le CPU est en train de devenir une
ressource gérée par la JVM (et le serveur
aussi !)
Les langages et les processeurs s’adaptent les
uns aux autres
204. Conclusion
Les applications vont utiliser des traitements
en parallèle de façon transparente
Mais les pièges se trouvent au niveau des
algorithmes, ils seront plus durs à éviter
Nouveaux challenges pour les développeurs,
et de nouvelles opportunités !