Modeling concurrency in Ruby and beyond<br />what is an advanced concurrency model?<br />Ilya Grigorik<br />@igrigorik<br />
“Concurrency is a property of systems in which several computations are executing simultaneously, and potential interactin...
Threads!<br />No. Events!<br />Neither. You need them both.<br />and neither is enough…<br />
2Ghz CPU = 0.5 ns cycle<br />RAM: 2000 wasted cycles!<br />~0.5 ns<br />~100 ns<br /><ul><li>Prefetching
Brand prediction
Instruction pipelining
Hyperthreading
Speculative execution
…</li></ul>~7 ns<br />Hardware Parallelism <br />maximizing resource utilization<br />http://bit.ly/cSKKVb<br />
if(cond1 && cond2) {<br />System.err.println("Am I faster yet?");<br />}<br />if (cond1 || cond2) {<br />System.err.printl...
Hardware Parallelism<br />Software Parallelism<br />(Processes, Threads, Events)<br />pthreads, lwkt, epoll, kqueue, …<br ...
Bruce: if you could go back in time, what is the one thing you would change?<br />Matz: “I would remove the thread and add...
Hardware Parallelism<br />Software Parallelism<br />(Processes, Threads, Events)<br />pthreads, lwkt, epoll, kqueue, …<br ...
Dataflow<br />Petri-nets<br />Actor Model<br />Transactional Memory<br />Pi-calculus / CSP<br />…<br />http://bit.ly/fMLJR...
The value of a tool / model is in:<br />what it enables you to do   <br />the constraints it imposes<br /><ul><li>Provide ...
Dictate a structure
Dictate a style
Disallow unwanted behavior
Implicitly “make the right choice”
Eliminate a class of errors</li></li></ul><li>“A Universal Modular Actor Formalism for Artificial Intelligence”<br />Carl ...
Give every process a name<br />Give every process a “mailbox”<br />Communicate via messages<br /><ul><li>A --> B</li></ul>...
Communication between: threads, processes, machines
Distributed programming</li></ul>Constraints:<br /><ul><li>No side-effects
No race conditions
No mutexes, no semaphores</li></ul>Actor Model<br />The 50k foot view…<br />
“Communicating sequential processes”<br />Hoare, C.A.R. (1978)<br />CCS, pi-calculus, …<br />…<br />Limbo (1995), Go (2007...
Processes are anonymous<br />Give every channel a name<br />Processes communicate over named channels<br /><ul><li>Think U...
Communication between: threads, processes, machines
Upcoming SlideShare
Loading in...5
×

Modeling concurrency in Ruby and beyond

4,146

Published on

The world of concurrent computation is a complicated one. We have to think about the hardware, the runtime, and even choose between half a dozen different models and primitives: fork/wait, threads, shared memory, message passing, semaphores, and transactions just to name a few. And that's only the beginning.

What's the state of the art for dealing with concurrency & parallelism in Ruby? We'll take a quick look at the available runtimes, what they offer, and their limitations. Then, we'll dive into the concurrency models and ask are threads really the best we can do to design, model, and test our software? What are the alternatives, and is Ruby the right language to tackle these problems?

Spoiler: out with the threads. Seriously.

Published in: Technology
1 Comment
10 Likes
Statistics
Notes
No Downloads
Views
Total Views
4,146
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
43
Comments
1
Likes
10
Embeds 0
No embeds

No notes for slide

Modeling concurrency in Ruby and beyond

  1. 1. Modeling concurrency in Ruby and beyond<br />what is an advanced concurrency model?<br />Ilya Grigorik<br />@igrigorik<br />
  2. 2. “Concurrency is a property of systems in which several computations are executing simultaneously, and potential interacting with each other.”<br />
  3. 3. Threads!<br />No. Events!<br />Neither. You need them both.<br />and neither is enough…<br />
  4. 4. 2Ghz CPU = 0.5 ns cycle<br />RAM: 2000 wasted cycles!<br />~0.5 ns<br />~100 ns<br /><ul><li>Prefetching
  5. 5. Brand prediction
  6. 6. Instruction pipelining
  7. 7. Hyperthreading
  8. 8. Speculative execution
  9. 9. …</li></ul>~7 ns<br />Hardware Parallelism <br />maximizing resource utilization<br />http://bit.ly/cSKKVb<br />
  10. 10. if(cond1 && cond2) {<br />System.err.println("Am I faster yet?");<br />}<br />if (cond1 || cond2) {<br />System.err.println("Am I fast yet?");<br />}<br />1<br />2<br />Turns out. We don’t know.<br />A quick poll<br />which is faster?<br />
  11. 11. Hardware Parallelism<br />Software Parallelism<br />(Processes, Threads, Events)<br />pthreads, lwkt, epoll, kqueue, …<br />C / C++, Java, Ruby, ….<br />The “concurrency API”<br />a bolt-on systems component for any language<br />
  12. 12. Bruce: if you could go back in time, what is the one thing you would change?<br />Matz: “I would remove the thread and add actors or some other more advanced concurrency features”<br />More advanced concurrency features?<br />
  13. 13. Hardware Parallelism<br />Software Parallelism<br />(Processes, Threads, Events)<br />pthreads, lwkt, epoll, kqueue, …<br />New!<br />“Advanced concurrency model”<br />C / C++, Java, Ruby, ….<br />
  14. 14. Dataflow<br />Petri-nets<br />Actor Model<br />Transactional Memory<br />Pi-calculus / CSP<br />…<br />http://bit.ly/fMLJR8<br />
  15. 15. The value of a tool / model is in:<br />what it enables you to do <br />the constraints it imposes<br /><ul><li>Provide a way to express a behavior
  16. 16. Dictate a structure
  17. 17. Dictate a style
  18. 18. Disallow unwanted behavior
  19. 19. Implicitly “make the right choice”
  20. 20. Eliminate a class of errors</li></li></ul><li>“A Universal Modular Actor Formalism for Artificial Intelligence”<br />Carl Hewitt; Peter Bishop and Richard Steiger (1973)<br />“Semantics of Communicating Parallel Professes”<br />Irene Grief (MIT EECS Doctoral Dissertation. August 1975)<br />…<br />Erlang (1986), Scala (2003), Kilim, …<br />The history:actor model<br />Let’s rewind back to the 1973 …<br />
  21. 21. Give every process a name<br />Give every process a “mailbox”<br />Communicate via messages<br /><ul><li>A --> B</li></ul>Enables:<br /><ul><li>Message centric view
  22. 22. Communication between: threads, processes, machines
  23. 23. Distributed programming</li></ul>Constraints:<br /><ul><li>No side-effects
  24. 24. No race conditions
  25. 25. No mutexes, no semaphores</li></ul>Actor Model<br />The 50k foot view…<br />
  26. 26. “Communicating sequential processes”<br />Hoare, C.A.R. (1978)<br />CCS, pi-calculus, …<br />…<br />Limbo (1995), Go (2007), CSP++, PyCSP…<br />The history:CSPmodel<br />Let’s rewind back to the 1978 …<br />
  27. 27. Processes are anonymous<br />Give every channel a name<br />Processes communicate over named channels<br /><ul><li>Think UNIX pipes…</li></ul>Enables:<br /><ul><li>Message centric view
  28. 28. Communication between: threads, processes, machines
  29. 29. Distributed programming</li></ul>Constraints:<br /><ul><li>No side-effects
  30. 30. No race conditions
  31. 31. No mutexes, no semaphores</li></ul>CSP / Pi-calculus<br />The 50k foot view…<br />
  32. 32. A<br />Multiple workers can share a channel<br /> A<br />Workers are mobile! Delegate the channel<br />to someone else!<br />C(A)<br />A<br /> A(B)<br />Send a “response” channel to another <br />process! <br />B<br />
  33. 33. gem install agent<br />let’s get hands on…<br />
  34. 34. Named channel<br />Typed channel<br />c =Agent::Channel.new(name: 'incr', type: Integer)<br />go(c) do |c, i=0|<br />loop { c <<i+= 1 }<br />end<br />p c.receive# => 1<br />p c.receive# => 2<br />Spawn the worker<br />Consume the results<br />Producer / Consumer<br />look, no threads!<br />
  35. 35. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker = Proc.new do |reqs|<br /> loop do<br />req = reqs.receive<br /> sleep 1.0<br />req.resultChan << [Time.now, req.args + 1].join(' : ')<br /> end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 = Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 = Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests << req1<br />clientRequests << req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />“Request” type<br />A “multi-threaded” server!<br />where’s the synchronization?<br />
  36. 36. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker =Proc.newdo |reqs|<br />loopdo<br />req=reqs.receive<br /> sleep 1.0<br />req.resultChan<< [Time.now, req.args+ 1].join(' : ')<br />end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 = Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 = Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests << req1<br />clientRequests << req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />wait for work<br />Sleep, increment, add timestamp<br />A “multi-threaded” server!<br />where’s the synchronization?<br />
  37. 37. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker =Proc.newdo |reqs|<br />loopdo<br />req=reqs.receive<br /> sleep 1.0<br />req.resultChan<< [Time.now, req.args+ 1].join(' : ')<br />end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 = Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 = Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests << req1<br />clientRequests << req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />Both workers listen on same channel<br />A “multi-threaded” server!<br />where’s the synchronization?<br />
  38. 38. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker =Proc.newdo |reqs|<br />loopdo<br />req=reqs.receive<br /> sleep 1.0<br />req.resultChan<< [Time.now, req.args+ 1].join(' : ')<br />end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 =Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 =Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests << req1<br />clientRequests << req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />Create two requests, each with return channel of type String<br />A “multi-threaded” server!<br />where’s the synchronization?<br />
  39. 39. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker =Proc.newdo |reqs|<br />loopdo<br />req=reqs.receive<br /> sleep 1.0<br />req.resultChan<< [Time.now, req.args+ 1].join(' : ')<br />end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 =Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 =Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests<< req1<br />clientRequests<< req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />Dispatch both requests<br />A “multi-threaded” server!<br />where’s the synchronization?<br />
  40. 40. Request =Struct.new(:args, :resultChan)<br />clientRequests=Agent::Channel.new(name: :clientRequests, type: Request, size: 2)<br />worker =Proc.newdo |reqs|<br />loopdo<br />req=reqs.receive<br /> sleep 1.0<br />req.resultChan<< [Time.now, req.args+ 1].join(' : ')<br />end<br />end<br /># start two workers<br />go(clientRequests, &worker)<br />go(clientRequests, &worker)<br />req1 =Request.new(1, Agent::Channel.new(:name => "resultChan-1", :type => String))<br />req2 =Request.new(2, Agent::Channel.new(:name => "resultChan-2", :type => String))<br />clientRequests<< req1<br />clientRequests<< req2<br />puts req1.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 2<br />puts req2.resultChan.receive # => 2010-11-28 23:31:08 -0500 : 3<br />A “multi-threaded” server!<br />where’s the synchronization?<br />Collect the results!<br />
  41. 41. So, Ruby?<br />JRuby, RBX, MacRuby, MRI, …<br />
  42. 42. JRuby:<br /><ul><li>No GIL
  43. 43. JVM threads
  44. 44. Existing libraries & frameworks: Akka, Kilim, etc
  45. 45. Great platform for experiments</li></ul>Rubinius:<br /><ul><li>Hydra branch: no GIL
  46. 46. Built in Channel / Actor primitives
  47. 47. Great platform to experiment withwith new language features</li></ul>MRI:<br /><ul><li>GIL
  48. 48. Research work on MVM
  49. 49. ... agent?</li></ul>MacRuby:<br /><ul><li>Grand Central Dispatch
  50. 50. MacRuby + IOS?
  51. 51. GCD + higher level API?</li></ul>The many Rubies…<br />for your concurrency experiments<br />
  52. 52. IO:<br /><ul><li>Small, compact, easy to learn
  53. 53. Actor based concurrency
  54. 54. http://iolanguage.com/</li></ul>Go:<br /><ul><li>Released by Google in ‘07
  55. 55. CSP + channels
  56. 56. http://golang.org/</li></ul>Clojure:<br /><ul><li>JVM + Functional programming
  57. 57. Transactional memory
  58. 58. http://clojure.org/</li></ul>Scala:<br /><ul><li>JVM
  59. 59. Actor based concurrency
  60. 60. http://www.scala-lang.org/</li></ul>… and many others …<br />Pick up & experiment with other runtimes!<br />learn what works, find what resonates…<br />
  61. 61. Hardware Parallelism<br />Software Parallelism<br />(Processes, Threads, Events)<br />pthreads, lwkt, epoll, kqueue, …<br />CSP / Actor / Dataflow / Transactional Memory<br />In Summary:<br /><ul><li>We need threads; we need events; we need locks; we need shared memory; …
  62. 62. Are threads, events, etc., the right API for modeling concurrency? Likely not.
  63. 63. Threads, events, etc., should belong under the hood.</li></li></ul><li>Multi-core, Threads & Message Passing:<br />http://www.igvita.com/2010/08/18/multi-core-threads-message-passing/<br />Concurrency with Actors, Goroutines & Ruby<br />http://www.igvita.com/2010/12/02/concurrency-with-actors-goroutines-ruby/<br />gem install agent<br />https://github.com/igrigorik/agent/<br />https://github.com/igrigorik/agent/tree/master/spec/<br />Phew, time for questions?<br />hope this convinced you to explore the area further…<br />
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×