4. LMAX :
Société spécialisée dans le passage d’ordres de bourse.
Affichage temps réel des cotations, volume de transactions élevé,
respect de l’ordonnancement des ordres et temps d’exécution
réduit.
7. Le cloud, j’le casse
- On se base sur le principe de l’IoA (Inversion of Architecture).
- Pourquoi ne pas utiliser le maximum de puissance CPU et la
mémoire de nos machines ?
- Toutes ces architectures par strates sont compliquées non ?
9. Mais pourquoi la gestion de la
concurrence en Java est si
compliquée ?
Attention, ces 384 pages sont dangereuses !!!
10. L’ordre :
// ordre de programmation // ordre d’exécution (non certain)
int w = 10; int y = 30;
int x = 20; int x = 20;
int y = 30; int b = x * y;
int z = 40;
int z = 40;
int a = w + z; int w = 10;
int b = x * y; int a = w + z;
De nombreux facteurs peuvent influer : le matériel, les
paramètres de la JVM, le type de GC,... merci je JIT
11. Avant de continuer quelques chiffres :
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns
Mutex lock/unlock 25 ns
Main memory reference 100 ns
Compress 1K bytes with Zippy 3,000 ns
Send 2K bytes over 1 Gbps network 20,000 ns
Read 1 MB sequentially from memory 250,000 ns
Round trip within same datacenter 500,000 ns
Disk seek 10,000,000 ns
Read 1 MB sequentially from disk 20,000,000 ns
Send packet CA->Netherlands->CA 150,000,000 ns
12. Il faut faire attention aux détails :
Incrémenter un compteur : Utiliser un lock :
public static long foo = 0;
static long foo = 0;
public static Lock lock = new Lock();
private static void increment() {
private static void increment() {
for (long l = 0; l < 500000000L; l++) {
for (long l = 0; l < 500000000L; l++) {
foo ++;
lock.lock();
}
try {
foo++;
} finally {
Utiliser un AtomicLong : lock.unlock();
}}}
static AtomicLong foo = new AtomicLong(0);
private static void increment() {
for (long l = 0; l < 500000000L; l++)
{foo.getAndIncrement();}
}
13. Le coût des contentions
Incrémenter un compteur 500 millions de fois...
● One Thread! :! 300 ms
● One Thread (volatile):! 4 700 ms (15x)
● One Thread (Atomic) : 5 700 ms (19x)
● One Thread (Lock)!: 10 000 ms (33x)
● Two Threads (Atomic) : 30 000 ms (100x)
● Two Threads (Lock)!: 224 000 ms (746x)
Soit pas loin de 4 minutes
14. Découper des chaînes de caractères
● Parallel (Scala): 440 ops/sec
● Serial! (Java) : 1768 ops/sec
La parallélisation des traitements n’est pas toujours une bonne solution
15. Et quid des processeurs ?
Comparaison du temps de découpage des chaines de caractères
en mono-thread sur différents processeurs.
Les performances peuvent aller du simple au double...
16. Mais que peut nous apporter le
Disruptor
... A part nous sauver des réplicateurs... (Private JOKE)
17. Ne pas devoir recourir à une architecture multi-thread
Donc, une mise en place plus aisée
De la simplicité pour les tests
Un coût de machines moins élevé
Se remettre en question sur le fonctionnement de Java...
18. Les problématiques rencontrées par la société LMAX
- False sharing
- Memory barrier
- Context switch
- CAS (compare and swap)
- Cache line
19. le False sharing
public void run()
{
long i = ITERATIONS + 1;
while (0 != --i)
{
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong
{
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6; // comment out
}
24. Les conclusions que l’on peut tirer...
Les queues (java) sont coûteuses (temps de traitement et lock des ressources)
Le multi-threading est coûteux (contentions sur les ressources communes) et difficile a mettre en œuvre.
Le multi-threading n’est pas le plus efficace en terme de performances.
Attention, ce cas d’utilisation ne peut pas convenir à tout le monde...
25. Ce qu’a fait LMAX :
Ecriture d’un framework permettant de partager rapidement les données entre thread sans toutefois
recourir aux mécanismes classiques des locks. C’est ce que nous nommerons Disruptor.
26. Le pattern Disruptor :
Les producteurs, le Ring Buffer et les consommateurs sont chacun dans des threads différents.
Les acteurs : Producteur, Consommateur
Les objets : RingBuffer, Barrier
Les stratégies : Wait, Claim
Objectif : ne plus avoir de contentions...
27. Alors qu’avec une architecture classique nous avions :
Mais où se trouve ma contention...
28. Ce que peut apporter le disruptor ?
C’est mieux mais toujours pas parfait....
29. Mais est-ce vraiment rapide ?
Conclusion : jusqu’à 5 fois plus rapide... et combien de fois moins cher (maintenance,
matériel,...) et plus constant...
31. Conclusion
- Ne jamais croire ce que l’on nous dit, il faut tester, expérimenter, la pratique est
souvent très loin de la théorie
- La concurrence est un outil à manier avec précautions,...
- Pour avoir des performances, chaque détail compte, mais comme le dit Donald
Knuth, «premature optimization is the root of Evil»