Comment mesurer les performances ? Comment savoir ce qu'il faut optimiser en premier ? Y'a-t-il des antipatterns à éviter ?
Nous allons vous présenter une démarche efficace pour mener vos campagnes de performance dans l'univers Java. Ponctuée de retours d'expérience, de best practices et de suggestions d'architecture, découvrez comment rendre vos applications plus performantes.
Par William Montaz et Jean-Pascal Thiery, consultants chez Xebia
2. #backdaybyxebia#perfEfficace @willymontaz @jpthiery
La performance, c’est quoi ?
« Mon Application, elle poutre ! »
« Il faut moins de 2 heures à mon batch pour
s`exécuter.»
« Mon algorithme utilise moins de 10% du CPU.»
- Ensemble de données chiffrables
- Des niveaux de tolérance qui dépendent du type de traitement.
23. #backdaybyxebia
Dead lock
Problème d’I/O :
Disque, Network, Système externe
Scheduling CPU par l’hyperviseur
Problème de scheduling OS
Je n’utilise pas 100% du CPU
OS
37. #backdaybyxebia
Coût de création des objets
Exemple :
Object foo = cache.get(val1 + ‘’-‘’ + val2)
Object foo = cache.get(new Key(val1, val2))
JVM
NB: en java 8, la concaténation de string est optimisée par le compilateur
55. #backdaybyxebia
GC Old Generation
Serial (-XX:+UseParallelGC)
Parallel (-XX:+UseParallelOldGC par défaut sur Java 7u4)
+ Pas d’overhead
+ Compaction de la heap
- Stop-of-the-world
Concurrent Mark-and-Sweep (CMS -XX:+UseConcMarkSweepGC)
+ (Presque) pas stop-of-the-world
- Overhead CPU (10-40%)
- Fragmentation
!! Peut déclencher des FullGC
JVM
56. #backdaybyxebia
Combinaison des GC
uint i = 0;
if (UseSerialGC) i++;
if (UseConcMarkSweepGC || UseParNewGC) i++;
if (UseParallelGC || UseParallelOldGC) i++;
if (UseG1GC) i++;
if (i > 1) {
jio_fprintf(defaultStream::error_stream(),
"Conflicting collector combinations in option list; "
"please refer to the release notes for the combinations "
"allowedn");
status = false;
}
JVM
60. #backdaybyxebia
JIT Friendly
Chauffer la JVM
Eviter les changements de class loaders
Choisir un GC adapté (throughput vs disponibilité)
Aider le GC si besoin (référence null)
Dimensionner correctement les pools
Notamment les survivors !
Utilisez les log GC
JVM
Ajout suite à la présentation -> Essayer de passer en Java 8
+15% de performances
Utilisation du GC G1 qui peut résoudre d’un coup beaucoup de problèmes de GC
64. #backdaybyxebia
Detecter les problèmes d’architecture/d’algorithme
Utiliser un profiler
VisualVM
JProfiler
Faire des benchmarks
Pas d’instrumentation
Reflet de la réalité (caches CPU, TLB, paging, …)
APP
65. #backdaybyxebia
Le benchmarking, c’est très dur !!
Optimisations JIT
Chauffe de la JVM
Profile-guided optims
Loop unrolling, Dead code
False sharing
Déclenchement de GC
Synchronisation des threads
Variance
APP
66. #backdaybyxebia
JMH
public class JMHSample_03_States {
@State(Scope.Thread)
public static class ThreadState {
volatile double x = Math.PI;
}
@Benchmark
public void measureUnshared(ThreadState state) {
state.x++;
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHSample_03_States.class.getSimpleName())
.warmupIterations(5)
.measurementIterations(5)
.threads(4)
.forks(1)
.build();
new Runner(opt).run();
}
}
APP
68. #backdaybyxebia
Problèmes fréquents
Pas de cache
Mauvaise utilisation des collections
Mauvaise implémentation :
ConcurrentHashMap
ConcurrencyLevel (LockStriping/Segment jdk < 1.8)
Lock
ConcurrentSkipListMap
LockFree
Pas de sizing initial
APP
69. #backdaybyxebia
Problèmes fréquents
Pas de cache
Mauvaise utilisation des collections
Mauvaise implémentation :
ConcurrentHashMap
ConcurrencyLevel (LockStriping/Segment jdk < 1.8)
Lock
ConcurrentSkipListMap
LockFree
Pas de sizing initial
Logs
Trop de logs
Trop de processing
Date -> timestamp
APP
70. #backdaybyxebia
Problèmes fréquents
Pas de cache
Mauvaise utilisation des collections
Mauvaise implémentation :
ConcurrentHashMap
ConcurrencyLevel (LockStriping/Segment jdk < 1.8)
Lock
ConcurrentSkipListMap
LockFree
Pas de sizing initial
Logs
Trop de logs
Trop de processing
Date -> timestamp
Serialisation
APP
71. #backdaybyxebia
Problèmes fréquents
Pas de cache
Mauvaise utilisation des collections
Mauvaise implémentation :
ConcurrentHashMap
ConcurrencyLevel (LockStriping/Segment jdk < 1.8)
Lock
ConcurrentSkipListMap
LockFree
Pas de sizing initial
Logs
Trop de logs
Trop de processing
Date -> timestamp
Serialisation
Pool sous/sur-dimensionné
Circuit breaker
APP
72. #backdaybyxebia
Pratiques Low Level:
NIO
epoll
FileChannels
Algos LockFree
Volatile, Compare And Swap
Affinité de cache
L1: 32KB 1ns (4 c), L2: 256KB 3-4ns (12 c), L3: plusieurs MB (30c) 12-15ns, RAM 70-100ns
Memory Acces Patterns :
affinité temporelle, affinité spaciale, predictible
Translation Lookaside Buffers (TLB)
Virtual Pages -> Physical pages in page table
TLB -> cache local pour éviter le parcours de la « page table »
Utiliser les Huge Pages (4MB) -> plus d’adresses mémoire dans les TLB
APP