programmation lock free - les techniques des pros (2eme partie)

56
@jpbempel #LockFree @jpbempel #LockFree Programmation Lock-Free : les techniques des pros Ullink Architecte Performance @jpbempel http://jpbempel.blogspot.com [email protected]

Upload: jean-philippe-bempel

Post on 15-Jul-2015

362 views

Category:

Software


0 download

TRANSCRIPT

@jpbempel#LockFree @jpbempel#LockFree

Programmation Lock-Free : les techniques des pros

UllinkArchitecte Performance@jpbempelhttp://[email protected]

@jpbempel#LockFree

Objectifs

Réduction de la contention pour une meilleure scalabilité

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

Précédemment...

@jpbempel#LockFree

Agenda 1ere partieMesurer la contention

Copy On Write

Lock striping

Compare-And-Swap

Introduction au Modèle Mémoire Java

@jpbempel#LockFree

Agenda 2eme partieDisruptor & RingBuffer

wait/notify – await/signal

Attente active (Spinning)

Queues lock-free (JCTools)

Ticketage : OrderedScheduler

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

Disruptor & Ring Buffer

@jpbempel#LockFree @jpbempel#LockFree

Librairie de LMAX (Incl. Martin Thompson)

Idées pas neuves, circulars buffers dans Kernel Linux

Portées sur Java

Disruptor

@jpbempel#LockFree @jpbempel#LockFree

Pourquoi ne pas utiliser la CLQ qui est Lock(Wait)-Free ?

•Queue non limitée (unbounded) et non bloquante•Alloue un node à chaque insertion (GC)•Pas très sympa avec le cache CPU•MultiProducteur/MultiConsommateur

Array/LinkedBlockingQueue: Pas Lock-free

Disruptor

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : 1P 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : 1P 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : 1P 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : 1P 1C

Object[] ringBuffer;volatile int head;volatile int tail;

public boolean offer(E e) { if (tail - head == ringBuffer.length) return false; ringBuffer[tail % ringBuffer.length] = e; tail++; // <- volatile write return true;}

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : 1P 1C

public E poll() { if (tail == head) // <- volatile read tail return null; int idx = head % ringBuffer.length; E element = ringBuffer[idx]; ringBuffer[idx] = null; head++; // <- volatile write return element;}

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

AtomicReferenceArray ringBuffer;AtomicLong head;AtomicLong tail;

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

public boolean offer(E e) { long curTail; do { curTail = tail.get() if (curTail - head.get() == ringBuffer.length) return false; } while (!tail.compareAndSet(curTail, curTail + 1)); ringBuffer.set(curTail % ringBuffer.length, e);//volatile write return true;}

@jpbempel#LockFree @jpbempel#LockFree

Ring Buffer : nP 1C

public E poll() { int index = head.get() % ringBuffer.length; E element = ringBuffer.get(index); if (element == null) return null; ringBuffer.set(index, null); head.set(head.get() + 1); // volatile write return element;}

@jpbempel#LockFree @jpbempel#LockFree

Grande flexibilité pour différents usages (Stratégies)

Très bonne performance

Transfert de données d’un thread à l’autre (Queue)

Disruptor

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

wait/notifyawait/signal

@jpbempel#LockFree @jpbempel#LockFree

Buffer vide, que fait le thread consommateur ?

Buffer plein, que fait(font) le(s) thread(s) producteur(s)

Insertion d’un élement, réveil du consommateur

wait/notify & await/signal

@jpbempel#LockFree @jpbempel#LockFree

Utilisé pour Guarded blocks ou variables conditionnelles

wait/notify & await/signal

@jpbempel#LockFree @jpbempel#LockFree

Latence + contention pour la mise en sommeil (wait)

Latence + contention pour le réveil

Latence pour le thread réveillé avant d’être schedulé

wait/notify & await/signal

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

Spinning

@jpbempel#LockFree @jpbempel#LockFree

Attente active

Très bon pour la réactivité du consommateur

Aucun coût pour le producteur

Brûle un cpu en permanence

Spinning

@jpbempel#LockFree @jpbempel#LockFree

Certains locks sont implémentés avec spin (spinLock)

Blocs Synchronized spinnent un peu sur contention

Utilisation de l’instruction pause (x86)

Spinning

@jpbempel#LockFree @jpbempel#LockFree

Comment éviter de brûler un core ?

Stratégie de dégagement:•Thread.yield()•LockSupport.parkNanos(1)•Object.wait()/Condition.await()/LockSupport.park()

Spinning: backoff

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

JCTools

@jpbempel#LockFree @jpbempel#LockFree

Librairie créée par Nitsan Wakart (Azul Systems)

Ensemble de Queues lock-free avec fin degré de choix pour différents usages

•Bounded/unbounded SPSC = Single Producer•Bounded/Unbounded MPSC = Multi Producer•Bounded SPMC/MPMC = Multi Consumer

JCTools

@jpbempel#LockFree @jpbempel#LockFree

Quel intéret d’avoir des type queues différentes ?

=> Optimisations en fonction des cas :

•masque pour modulo•volatile writes (lazySet)•false sharing•memory layout•unsafe

JCTools

@jpbempel#LockFree @jpbempel#LockFree

masque pour modulo•Tableau de puissance de 2•Appliquer un et binaire au lieu d'un modulo

volatile writes (lazySet)•Pas de barrière matériel

JCTools : Optimisations

@jpbempel#LockFree @jpbempel#LockFree

False sharing

Champs distincts sur même ligne de cache (64 octets)

=> Padding

JCTools : Optimisations

@jpbempel#LockFree @jpbempel#LockFree

Memory Layout

Champs sont réorganisés par taille et alignésUtilisation de l'héritage pour s'assurer de l'ordre des champs

JCTools : Optimisations

class A { int f1;}

class B extends A { long f2;}

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

Ticketage :OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

Comment paralléliser des tâches tout en gardant l’ordre ?

exemple: traitement d’un flux video•Lecture d’une image du flux •Traitement de l’image (parallélisable)•écriture du flux transformé (dans l’ordre)

Ticketage

@jpbempel#LockFree @jpbempel#LockFree

Ticketage

@jpbempel#LockFree @jpbempel#LockFree

Ticketage

@jpbempel#LockFree @jpbempel#LockFree

Possible de le faire avec Disruptor,mais par un thread consommateur dédié

OrderedScheduler permet de le faire sans :•pas de surcoût communication inter-threads•pas de thread additionnel •pas de stratégie d’attente

L’idée c’est de prendre un ticket...

Ticketage

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

// called by multiple threads (e.g. thread pool)public void execute() { synchronized (this) { FooInput input = read(); BarOutput output = process(input); write(output); }}

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

OrderedScheduler scheduler = new OrderedScheduler();

public void execute() { FooInput input; long ticket; synchronized (this) { input = read(); ticket = scheduler.getNextTicket(); }[...]

@jpbempel#LockFree @jpbempel#LockFree

OrderedScheduler

public void execute() {[...] BarOutput output; try { output = process(input); } catch (Exception ex) { scheduler.trash(ticket); throw new RuntimeException(ex); } scheduler.run(ticket, () => { write(output); });}

@jpbempel#LockFree @jpbempel#LockFree

open source sur GitHub

Ouvert aux PR et aux discussions sur le design

Utilisé en interne

Venez faire de la code review au stand Ullink !

OrderedScheduler

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

Ce qu’il faut retenir

@jpbempel#LockFree @jpbempel#LockFree

RingBuffer: lock-free facile

Wait!

Spin

JCTools

Ordonner sans thread consommateur

Ce qu’il faut retenir

@jpbempel#LockFree

Références•circular buffers: http://lwn.net/Articles/378262/•Mr T queues: https://github.com/mjpt777/examples/tree/master/src/java/uk/co/real_logic/queues

•Lock-free algorithms by Mr T: http://www.infoq.com/presentations/Lock-free-Algorithms

•Futex are tricky U. Drepper: http://www.akkadia.org/drepper/futex.pdf•JCTools: https://github.com/JCTools/JCTools•Nitsan Wakart blog: http://psy-lob-saw.blogspot.com

@jpbempel#LockFree

Références•Exploiting data parallelism in ordered data streams: https://software.intel.com/en-us/articles/exploiting-data-parallelism-in-ordered-data-streams

•OrderedScheduler: https://github.com/Ullink/ordered-scheduler•Concurrency Freaks: http://concurrencyfreaks.blogspot.com•Preshing on programming: http://preshing.com/•Is Parallel Programming Hard, And If So, What Can You Do About It? https://kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2015.01.31a.pdf

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

Q & A

UllinkArchitecte Performance@jpbempelhttp://[email protected]