Reagents:
Functional programming meets scalable concurrency
Aaron Turon
Northeastern University
Saturday, November 10, 12
Reagents: Functional programming meets scalable concurrency Aaron - - PowerPoint PPT Presentation
Reagents: Functional programming meets scalable concurrency Aaron Turon Northeastern University Saturday, November 10, 12 Concurrency Parallelism Concurrency is overlapped execution of processes. Parallelism is simultaneous execution of
Northeastern University
Saturday, November 10, 12
Concurrency is overlapped execution of processes. Parallelism is simultaneous execution of computations.
Saturday, November 10, 12
The trouble is that essentially all the interesting applications of concurrency involve the deliberate and controlled mutation of shared state, such as screen real estate, the file system, or the internal data structures of the program. The right solution, therefore, is to provide mechanisms which allow (though alas they cannot enforce) the safe mutation of shared state.
in Concurrent Haskell
Saturday, November 10, 12
(e.g. OS kernels)
(e.g. work stealing for data parallelism)
(where we must resort to concurrency)
Use cases:
Saturday, November 10, 12
class LockCounter { private var c: Int = 0 private var l = new Lock def inc: Int = { l.lock() val old = c c = old + 1 l.unlock()
} }
Saturday, November 10, 12
class CASCounter { private var c = new AtomicRef[Int](0) def inc: Int = { while (true) { val old = c if (c.cas(old, old+1)) return old } } }
Saturday, November 10, 12
Saturday, November 10, 12
Threads Throughput 1 8 Predicted CAS Locking
Saturday, November 10, 12
Lock-based CAS-based
Threads
2 4 6 8
Threads
2 4 6 8
Parallelism (log-scale)
63% 88% 98% 99.7% 99.9%
Throughput Optimal
1.0 0.87 0.74 0.61 0.48 0.35
Saturday, November 10, 12
Saturday, November 10, 12
Cost Coarse-grained Fine-grained
Saturday, November 10, 12
Nehalem Quadcore Core 0 Shared Level 3 Cache IMC (3 Channel) QPI L1 Core 1 Core 2 Core 3 L2 L2 L2 L2 I/O Hub L1 L1 L1 Nehalem Quadcore Core 4 Shared Level 3 Cache QPI L1 Core 5 Core 6 Core 7 L2 L2 L2 L2 L1 L1 L1 DDR3 A IMC (3 Channel) DDR3 C DDR3 B DDR3 D DDR3 F DDR3 E
Saturday, November 10, 12
Saturday, November 10, 12
Synchronization Data structures
Reentrant locks Semaphores R/W locks Reentrant R/W locks Condition variables Countdown latches Cyclic barriers Phasers Exchangers Queues Nonblocking Blocking (array & list) Synchronous Priority, nonblocking Priority, blocking Deques Sets Maps (hash & skiplist)
Saturday, November 10, 12
class TreiberStack[A] { private val head = new AtomicRef[List[A]](Nil) def push(a: A) { val backoff = new Backoff while (true) { val cur = head.get() if (head.cas(cur, a :: cur)) return backoff.once() } } ...
Saturday, November 10, 12
3 2
Head
Saturday, November 10, 12
3 2
Head
7
Saturday, November 10, 12
3 2
Head
7 5
Saturday, November 10, 12
3 2
Head
7 5
CAS fail
Saturday, November 10, 12
3 2
Head
7 5
Saturday, November 10, 12
3 2
Head
7 5
Saturday, November 10, 12
def tryPop(): Option[A] = { val backoff = new Backoff while (true) { val cur = head.get() cur match { case Nil => return None case a::tail => if (head.cas(cur, tail)) return Some(a) } backoff.once() } }
Saturday, November 10, 12
Saturday, November 10, 12
Saturday, November 10, 12
Saturday, November 10, 12
f
A B
Lambda abstraction:
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R
Saturday, November 10, 12
c: Chan[A,B]
c
swap
A B
Saturday, November 10, 12
c: Chan[A,B]
c
swap
A B
c
swap
B A
Saturday, November 10, 12
c: Chan[A,B]
c
swap
A B
Saturday, November 10, 12
swap
Message passing
Saturday, November 10, 12
swap
r: Ref[A] f: (A,B)→(A,C)
upd
f
r
A A B C
Message passing
Saturday, November 10, 12
swap upd
f
Message passing Shared state
Saturday, November 10, 12
swap upd
f A B
R
A B
S
Message passing Shared state
Saturday, November 10, 12
swap upd
f
R S
+
A B
Message passing Shared state
Saturday, November 10, 12
swap upd
f
R S
+
Message passing Shared state Disjunction
Saturday, November 10, 12
swap upd
f
R S
+
A B
R
A C
S
Message passing Shared state Disjunction
Saturday, November 10, 12
swap upd
f
R S
+
R S
*
A (B,C)
Message passing Shared state Disjunction
Saturday, November 10, 12
swap upd
f
R S
+
R S
*
Message passing Shared state Disjunction Conjunction
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R application:
f(a) = b
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R application:
f(a) = b
apply as reactant:
R ! a = b
Saturday, November 10, 12
f
A B
Lambda abstraction: Reagent abstraction:
A B
R application:
f(a) = b
apply as reactant:
R ! a = b
apply as catalyst:
dissolve(R)
Saturday, November 10, 12
c: Chan[A,B]
c
swap
A B
c
swap
B A
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
!() !3
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
() 3
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
dissolve !3
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
() !3 ...()()()
Saturday, November 10, 12
c: Chan[Unit,Int]
c
swap Unit
c
swap Unit Int Int
() 3 ...()()()
Saturday, November 10, 12
d
swap
A
c
swap
Unit Unit
Saturday, November 10, 12
d
swap
A
c
swap
Unit Unit
“Receive” “Send”
Saturday, November 10, 12
d
swap
A
c
swap
“Receive” “Send”
Saturday, November 10, 12
d
swap
A
c
swap
Saturday, November 10, 12
d
swap
A
c
swap
NB: transfer is atomic
Saturday, November 10, 12
d
swap *
c
swap
A B (A,B)
Saturday, November 10, 12
d
swap *
c
swap
A B (A,B)
Saturday, November 10, 12
d
swap *
c
swap
A B (A,B)
+
e
swap
Exn
Saturday, November 10, 12
d
swap *
c
swap
A B (A,B)
+
e
swap
Exn
Saturday, November 10, 12
c1(x1) & · · · & cn(xn) ⇒ e
Saturday, November 10, 12
c1(x1) & · · · & cn(xn) ⇒ e
(swap c1 * · · · * swap cn)
>>> postCommit e
becomes
Saturday, November 10, 12
c1(x1) & · · · & cn(xn) ⇒ e
(swap c1 * · · · * swap cn)
>>> postCommit e
becomes
dissolve(
Saturday, November 10, 12
class TreiberStack [A] { private val head = new Ref[List[A]](Nil) val push : A ↣ () = upd(head)(cons) val tryPop : () ↣ A? = upd(head) { case (x :: xs) => (xs, Some(x)) case Nil => (Nil, None) } }
Saturday, November 10, 12
class TreiberStack [A] { private val head = new Ref[List[A]](Nil) val push : A ↣ () = upd(head)(cons) val tryPop : () ↣ A? = upd(head) { case (x :: xs) => (xs, Some(x)) case Nil => (Nil, None) } val pop : () ↣ A = upd(head) { case (x :: xs) => (xs, x) } }
Saturday, November 10, 12
class TreiberStack [A] { private val head = new Ref[List[A]](Nil) val push = upd(head)(cons) val tryPop = upd(head)(trySplit) val pop = upd(head)(split) }
Saturday, November 10, 12
class TreiberStack [A] { private val head = new Ref[List[A]](Nil) val push = upd(head)(cons) val tryPop = upd(head)(trySplit) val pop = upd(head)(split) } class EliminationStack [A] { private val stack = new TreiberStack[A] private val (send, recv) = new Chan[A] val push = stack.push + swap(send) val pop = stack.pop + swap(recv) }
Saturday, November 10, 12
stack1.pop >>> stack2.push
Saturday, November 10, 12
3 2
Head
5
Tail
Saturday, November 10, 12
3 2
Head
5
Tail
computed: A → (() ↣ B) → (A ↣ B)
Saturday, November 10, 12
Use invisible side-effects to traverse the queue while computing the upd
Saturday, November 10, 12
Saturday, November 10, 12
Phase 1 Phase 2
Saturday, November 10, 12
Phase 1 Phase 2 Accumulate CASes
Saturday, November 10, 12
Phase 1 Phase 2 Accumulate CASes Attempt k-CAS
Saturday, November 10, 12
Accumulate CASes Attempt k-CAS
Saturday, November 10, 12
Accumulate CASes Attempt k-CAS
Permanent failure
Saturday, November 10, 12
Accumulate CASes Attempt k-CAS
Permanent failure Transient failure
Saturday, November 10, 12
Saturday, November 10, 12
Permanent failure
Saturday, November 10, 12
Permanent failure Transient failure
Saturday, November 10, 12
Permanent failure Transient failure Transient failure
Saturday, November 10, 12
Permanent failure Transient failure ? failure Transient failure
Saturday, November 10, 12
Permanent failure Transient failure ? failure Transient failure
P & P = P T & T = T P & T = T T & P = T
Saturday, November 10, 12
Saturday, November 10, 12
So the “redo log” is write-only for phase 1! Therefore: pay-as-you-go
Saturday, November 10, 12
Isolation Shared state Interaction Message passing
Saturday, November 10, 12
Isolation Shared state Interaction Message passing Using lock-free bags, based on earlier work with Russo [OOPSLA’11]
Saturday, November 10, 12
Throughput (iters/μs)
Saturday, November 10, 12
Throughput (iters/μs)
Saturday, November 10, 12
isolation?
Saturday, November 10, 12
message-passing?
Saturday, November 10, 12
Joins CML STM
Saturday, November 10, 12
Joins CML STM
Transactional events Communicating transactions
Saturday, November 10, 12