programmation lock free - les techniques des pros (1ere partie)

Post on 17-Jul-2015

453 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

@jpbempel#LockFree @jpbempel#LockFree

Programmation Lock-Free : les techniques des pros

UllinkArchitecte Performance@jpbempelhttp://jpbempel.blogspot.comjpbempel@ullink.com

@jpbempel#LockFree

Objectif

Réduction de la contention pour une meilleure scalabilité

@jpbempel#LockFree

Agenda 1ère partieMesurer la contention

Copy On Write

Lock striping

Compare-And-Swap

Introduction au Modèle Mémoire Java

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Immutabilité

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Contention

@jpbempel#LockFree @jpbempel#LockFree

Contention2 threads ou plus essayant d'acquérir un lock

Threads en attente de la libération du lock

Raison numéro 1 pour éviter les locks

Photo de Filip Bogdan

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Measure, don’t guess!Kirk Pepperdine & Jack Shirazi

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Measure, don’t premature!

@jpbempel#LockFree @jpbempel#LockFree

Bloc Synchronized

•Profilers (YourKit, JProfiler, ZVision)•JVMTI native agent•Résultats peuvent être difficiles à exploiter

Mesurer la contention

@jpbempel#LockFree @jpbempel#LockFree

Mesurer la contention

@jpbempel#LockFree @jpbempel#LockFree

Mesurer la contention

@jpbempel#LockFree @jpbempel#LockFree

Mesurer la contentionjava.util.concurrent.Lock

•JVM ne peut aider ici•classes du JDK, code classique•JProfiler peut les profiler•Modification classes j.u.c + bootclasspath (jucProfiler)

@jpbempel#LockFree @jpbempel#LockFree

Mesurer la contention

@jpbempel#LockFree @jpbempel#LockFree

Mesurer la contentionInsertion de compteur de contention•Identifier les endroits où l’acquisition des locks échouent •Incrémenter le compteur

Identifier les locks•Callstack à l’instanciation•logger le statut des compteurs

Comment mesurer les locks existants dans votre code•Modifier les classes du JDK•Réintroduire ces classes dans les bootclasspath

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Copy On Write

@jpbempel#LockFree @jpbempel#LockFree

Copier toutes les données quand il y a modification

Très simple, même chose que l’immutabilité

Même problèmes que l’immutabilité (surcoût copie, GC)

Fonctionne bien pour structures à faible modification

Copy On Write

@jpbempel#LockFree @jpbempel#LockFree

Meilleur exemple dans JDK: CopyOnWriteArrayList

Parfait pour les listes de listeners

Autre usage interne: CopyOnWriteHashMap

Maps de référence

Copy On Write

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Lock striping

@jpbempel#LockFree @jpbempel#LockFree

Réduire la contention en la distribuant

N’enlève pas de locks, mais en ajoute !

Efficacité dépend d’un bon partitionnement (cf HashMap)

Lock striping

@jpbempel#LockFree @jpbempel#LockFree

Meilleur exemple dans JDK: ConcurrentHashMap [JDK7]

Lock striping

@jpbempel#LockFree @jpbempel#LockFree

Relativement simple à implémenter

Peut être très efficace si bon partitionnement

Peut être affiné (nombre de partitions) en fonction de la contention/niveau de concurrence

Lock striping

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Compare-And-Swap

@jpbempel#LockFree @jpbempel#LockFree

Primitive pour n’importe quel algorithme Lock-Free

Utilisé pour implémenter locks et primitives de synchronisations

Géré directement par le CPU (instruction dédiée)

Compare-And-Swap

@jpbempel#LockFree @jpbempel#LockFree

Met à jour de façon atomique un emplacement mémoire par une valeur si la précédente est celle attendue

instruction à 3 arguments:•adresse mémoire (rbx)•valeur attendue (rax)•nouvelle valeur (rcx)

Compare-And-Swap

movabs rax,0x2a movabs rcx,0x2b lock cmpxchg QWORD PTR [rbx],rcx

@jpbempel#LockFree @jpbempel#LockFree

Compare-And-Swap

@jpbempel#LockFree @jpbempel#LockFree

En Java pour les classes AtomicXXX :

L’adresse mémoire est le champ value de la classe

Compare-And-Swap

boolean compareAndSet(expect, update)

@jpbempel#LockFree @jpbempel#LockFree

Incrément atomique avec CAS[JDK7] getAndIncrement():

[JDK8] getAndIncrement():

“intrinsèquifier”:

Compare-And-Swap: AtomicLong

while (true) { long current = get(); long next = current + 1; if (compareAndSet(current, next)) return current; }

return unsafe.getAndAddLong(this, valueOffset, 1L);

movabs rsi,0x1 lock xadd QWORD PTR [rdx+0x10],rsi

@jpbempel#LockFree @jpbempel#LockFree

Echange atomique avec CAS[JDK7] long getAndSet(long newValue):

[JDK8] long getAndSet(long newValue):

“intrinsèquifier”:

Compare-And-Swap: AtomicLong

while (true) { long current = get(); if (compareAndSet(current, newValue)) return current; }

return unsafe.getAndSetLong(this, valueOffset, newValue);

xchg QWORD PTR [rsi+0x10],rdi

@jpbempel#LockFree @jpbempel#LockFree

ReentrantLock est implémenté avec CAS

Si CAS échoue => Lock déjà acquis

Compare-And-Swap: lock

volatile int state;

lock() compareAndSet(0, 1);

unlock(): setState(0);

@jpbempel#LockFree @jpbempel#LockFree

L’algorithme lock-free le plus simple

Utilise CAS pour mettre à jour le pointeur suivant dans la liste chainée

Si CAS échoue, une mise à jour concurrente a eu lieu

Lire la nouvelle valeur, aller à l’élément suivant et renouveler le CAS

Compare-And-Swap: ConcurrentLinkedQ

@jpbempel#LockFree @jpbempel#LockFree

Compare-And-Swap: ConcurrentLinkedQ

@jpbempel#LockFree @jpbempel#LockFree

Compare-And-Swap: ConcurrentLinkedQ

@jpbempel#LockFree @jpbempel#LockFree

Compare-And-Swap: ConcurrentLinkedQ

@jpbempel#LockFree @jpbempel#LockFree

Compare-And-Swap: ConcurrentLinkedQ

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Modèle Mémoire Java(Introduction)

@jpbempel#LockFree @jpbempel#LockFree

Premier langage à avoir un modèle mémoire bien défini:Java JDK5 (2004) avec le JSR 133

C++ obtient le sien en 2011 (C++11)

Avant cela, certaines constructions sont non définies ou différentes en fonction de la plateforme/compilateur (ex : Double Check Locking)

Modèle Mémoire Java

@jpbempel#LockFree @jpbempel#LockFree

Ordonnancement mémoire int a; int b; boolean enabled;

{ { a = 21; enabled = true; b = a * 2; a = 21; enabled = true; b = a * 2; } }

JIT Compiler

@jpbempel#LockFree @jpbempel#LockFree

Ordonnancement mémoire int a; int b; boolean enabled;

Thread 1 Thread 2 { { a = 21; if (enabled) { b = a * 2; int answer = b; enabled = true; process(answer); } } }

@jpbempel#LockFree @jpbempel#LockFree

Ordonnancement mémoire int a; int b; volatile boolean enabled;

Thread 1 Thread 2 { { a = 21; if (enabled) { b = a * 2; int answer = b; enabled = true; process(answer); } } }

@jpbempel#LockFree @jpbempel#LockFree

Peut être à 2 niveaux: Compilateur & Hardware

En fonction de l’architecture du CPU, la barrière n’est pas forcément requise

x86: Modèle fort, réordonnancement limité

Barrière Mémoire

@jpbempel#LockFree @jpbempel#LockFree

Barrière mémoire

@jpbempel#LockFree @jpbempel#LockFree

Champ volatile implique des barrières mémoires

Barrière compilateur : empêche réordonnancement

Barrière matérielle : S’assure que les buffers sont vidés

Sur x86, seule la barrière d’écriture est nécessaire

Barrière mémoire: volatile

lock add DWORD PTR [rsp],0x0

@jpbempel#LockFree @jpbempel#LockFree

CAS est aussi une barrière mémoire

Compilateur : Reconnu par le JIT (Unsafe)

Matérielle : toutes les instructions lock agissent comme une barrière mémoire

Barrière mémoire: CAS

@jpbempel#LockFree @jpbempel#LockFree

blocs synchronized ont aussi des barrières mémoires

Entrée du bloc: Barrière mémoire de lecture

Sortie du bloc: Barrière mémoire d’écriture

Barrière mémoire: synchronized

synchronized (this) { enabled = true; a = 21; b = a * 2; }

@jpbempel#LockFree @jpbempel#LockFree

Méthode des classes AtomicXXX

Barrière mémoire seulement pour le compilateur

Pas de barrière mémoire matérielle pour l’écriture

Garantit toujours l’ordonnancement, mais l’effet ne sera pas immédiat pour les autres threads

Barrière mémoire: lazySet

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Ce qu’il faut retenir

@jpbempel#LockFree @jpbempel#LockFree

Mesurer

Copier

Distribuer

Mettre à jour atomiquement

Ordonner

Ce qu’il faut retenir

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

A suivre...

@jpbempel#LockFree @jpbempel#LockFree

Disruptor & Ring Buffer

wait/notify – await/signal

Attente active (Spinning)

Queues lock-free (JCTools)

Ticketage : OrderedScheduler

A suivre dans la 2ème partie

@jpbempel#LockFree

Références•jucProfiler: http://www.infoq.com/articles/jucprofiler•Java Memory Model Pragmatics: http://shipilev.net/blog/2014/jmm-pragmatics/

•Memory Barriers and JVM Concurrency: http://www.infoq.com/articles/memory_barriers_jvm_concurrency

•JSR 133 (FAQ): http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

•CPU cache flushing fallacy: http://mechanical-sympathy.blogspot.fr/2013/02/cpu-cache-flushing-fallacy.html

•atomic<> Weapons: http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2

@jpbempel#LockFree @YourTwitterHandle@YourTwitterHandle@jpbempel#LockFree

Q & A

UllinkArchitecte Performance@jpbempelhttp://jpbempel.blogspot.comjpbempel@ullink.com

top related