reagents
play

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


  1. Reagents: Functional programming meets scalable concurrency Aaron Turon Northeastern University Saturday, November 10, 12

  2. Concurrency ≠ Parallelism Concurrency is overlapped execution of processes. Parallelism is simultaneous execution of computations. Saturday, November 10, 12

  3. 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. -- Peyton Jones, Gordon, and Finne in Concurrent Haskell Saturday, November 10, 12

  4. Concurrency ⋂ Parallelism = Scalable Concurrency Use cases: • Concurrent programs on parallel hardware (e.g. OS kernels) • Implementing parallel abstractions (e.g. work stealing for data parallelism) • “Last mile” of parallel programming (where we must resort to concurrency) Saturday, November 10, 12

  5. 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() old } } Saturday, November 10, 12

  6. 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

  7. A simple test • Increment counter • Busywait for t cycles (no cache interaction) • Repeat Saturday, November 10, 12

  8. Results for 98% parallelism Predicted Throughput CAS Locking 1 8 Threads Saturday, November 10, 12

  9. Lock-based CAS-based 99.9% Parallelism (log-scale) 99.7% 98% 88% 63% 2 4 6 8 2 4 6 8 Threads Threads Throughput Optimal 1.0 0.87 0.74 0.61 0.48 0.35 Saturday, November 10, 12

  10. What’s going on here? Saturday, November 10, 12

  11. What’s going on here? Communication Cost Coarse-grained Fine-grained Saturday, November 10, 12

  12. Nehalem Quadcore Nehalem Quadcore Core 0 Core 1 Core 2 Core 3 Core 4 Core 5 Core 6 Core 7 L1 L1 L1 L1 L1 L1 L1 L1 L2 L2 L2 L2 L2 L2 L2 L2 Shared Level 3 Cache Shared Level 3 Cache IMC IMC QPI QPI (3 Channel) (3 Channel) DDR3 C DDR3 D DDR3 A DDR3 B DDR3 E DDR3 F I/O Hub Saturday, November 10, 12

  13. Saturday, November 10, 12

  14. java.util.concurrent Synchronization Data structures Reentrant locks Queues Semaphores Nonblocking R/W locks Blocking (array & list) Reentrant R/W locks Synchronous Condition variables Priority, nonblocking Countdown latches Priority, blocking Cyclic barriers Deques Phasers Sets Exchangers Maps (hash & skiplist) Saturday, November 10, 12

  15. 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

  16. Head 3 2 Saturday, November 10, 12

  17. Head 3 2 7 Saturday, November 10, 12

  18. Head 5 3 2 7 Saturday, November 10, 12

  19. Head 5 3 2 CAS fail 7 Saturday, November 10, 12

  20. Head 5 3 2 7 Saturday, November 10, 12

  21. Head 5 3 2 7 Saturday, November 10, 12

  22. 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

  23. The Problem: Concurrency libraries are indispensable, but hard to build and extend Saturday, November 10, 12

  24. The Proposal: Build and extend scalable concurrent algorithms using a monad with shared-state and message-passing operations Saturday, November 10, 12

  25. Design Saturday, November 10, 12

  26. Reagents are (first) arrows A B Lambda abstraction: f Saturday, November 10, 12

  27. Reagents are (first) arrows A B Lambda abstraction: f A B Reagent abstraction: R Saturday, November 10, 12

  28. c : Chan[A,B] B A swap c Saturday, November 10, 12

  29. c : Chan[A,B] B A swap c c swap B A Saturday, November 10, 12

  30. c : Chan[A,B] B A swap c Saturday, November 10, 12

  31. Message passing swap Saturday, November 10, 12

  32. r : Ref[A] Message passing f : (A,B) → (A,C) upd B C swap f r A A Saturday, November 10, 12

  33. Message passing Shared state upd swap f Saturday, November 10, 12

  34. Message passing Shared state upd swap f A B R A B S Saturday, November 10, 12

  35. Message passing Shared state upd swap f R A B + S Saturday, November 10, 12

  36. Message passing Shared state upd swap f Disjunction R + S Saturday, November 10, 12

  37. Message passing Shared state upd swap f Disjunction A B R R + A C S S Saturday, November 10, 12

  38. Message passing Shared state upd swap f Disjunction R R A (B,C) + * S S Saturday, November 10, 12

  39. Message passing Shared state upd swap f Conjunction Disjunction R R + * S S Saturday, November 10, 12

  40. A B Lambda abstraction: f A B Reagent abstraction: R Saturday, November 10, 12

  41. A B Lambda abstraction: f A B Reagent abstraction: R Saturday, November 10, 12

  42. A B Lambda abstraction: f application: f(a) = b A B Reagent abstraction: R Saturday, November 10, 12

  43. A B Lambda abstraction: f application: f(a) = b A B Reagent abstraction: R apply as reactant: R ! a = b Saturday, November 10, 12

  44. A B Lambda abstraction: f application: f(a) = b A B Reagent abstraction: R apply as reactant: R ! a = b apply as catalyst: dissolve(R) Saturday, November 10, 12

  45. c : Chan[A,B] B A swap c c swap B A Saturday, November 10, 12

  46. c : Chan[Unit,Int] Unit Int swap c c swap Unit Int Saturday, November 10, 12

  47. c : Chan[Unit,Int] Unit Int !() swap c c !3 swap Unit Int Saturday, November 10, 12

  48. c : Chan[Unit,Int] Unit Int 3 swap c c () swap Unit Int Saturday, November 10, 12

  49. c : Chan[Unit,Int] Unit Int swap dissolve c c !3 swap Unit Int Saturday, November 10, 12

  50. c : Chan[Unit,Int] Unit Int ...()()() () swap c c !3 swap Unit Int Saturday, November 10, 12

  51. c : Chan[Unit,Int] Unit Int ...()()() 3 swap c c () swap Unit Int Saturday, November 10, 12

  52. Unit A Unit swap swap c d Saturday, November 10, 12

  53. “Receive” “Send” Unit A Unit swap swap c d Saturday, November 10, 12

  54. “Receive” “Send” A swap swap c d Saturday, November 10, 12

  55. Pipeline catalyst A swap swap c d Saturday, November 10, 12

  56. Pipeline catalyst A swap swap c d NB: transfer is atomic Saturday, November 10, 12

  57. (A,B) swap swap * c d A B Saturday, November 10, 12

  58. 2-way join (A,B) swap swap * c d A B Saturday, November 10, 12

  59. 2-way join (A,B) ( ) swap swap + swap * c d e Exn A B Saturday, November 10, 12

  60. Abortable 2-way join (A,B) ( ) swap swap + swap * c d e Exn A B Saturday, November 10, 12

  61. Join Calculus c 1 ( x 1 ) & · · · & c n ( x n ) ⇒ e Saturday, November 10, 12

  62. Join Calculus c 1 ( x 1 ) & · · · & c n ( x n ) ⇒ e becomes ( swap c 1 * · · · * swap c n ) >>> postCommit e Saturday, November 10, 12

  63. Join Calculus c 1 ( x 1 ) & · · · & c n ( x n ) ⇒ e becomes dissolve ( ) ( swap c 1 * · · · * swap c n ) >>> postCommit e Saturday, November 10, 12

  64. 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

  65. 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

  66. 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

  67. 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

  68. stack1.pop >>> stack2.push Saturday, November 10, 12

  69. Going Monadic Head Tail X 5 3 2 Saturday, November 10, 12

  70. Going Monadic Head Tail X 5 3 2 computed: A → (() ↣ B) → (A ↣ B) Saturday, November 10, 12

  71. Use invisible side-effects to traverse the queue while computing the upd operation to perform Saturday, November 10, 12

Recommend


More recommend