Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RubyConf.ph 2014 - ZOMGSCALE! With Celluloid & JRuby

RubyConf.ph 2014 - ZOMGSCALE! With Celluloid & JRuby

Ben Lovell

March 29, 2014
Tweet

More Decks by Ben Lovell

Other Decks in Programming

Transcript

  1. zomgscale!
    with Celluloid and JRuby
    Ben Lovell

    View Slide

  2. benlovell
    _j

    View Slide

  3. 113581334398839860922
    !

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. “I HAVE KILLED, AND I WILL KILL AGAIN”

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. View Slide


  26. HEY
    LADIES

    View Slide


  27. ❤️

    View Slide

  28. View Slide

  29. View Slide

  30. zomgscale!
    with Celluloid and JRuby
    Ben Lovell

    View Slide

  31. Moore’s
    Law

    View Slide

  32. View Slide

  33. I don’t like bungee
    jumping...
    !
    ...but I do like skiing
    Roger Moore

    View Slide

  34. View Slide

  35. every few years
    CPU clocks
    have doubled

    View Slide

  36. but recently
    this growth
    has stalled

    View Slide

  37. cores
    ++++++++

    View Slide

  38. the free lunch
    is over
    Herb Sutter

    View Slide

  39. harness the
    POWER
    in those cores

    View Slide

  40. ?

    View Slide

  41. concurrency
    parallelism

    View Slide

  42. so there’s a
    difference?

    View Slide

  43. concurrent!

    View Slide

  44. parallel?

    View Slide

  45. View Slide

  46. processes
    or
    threads

    View Slide

  47. processes
    memory constraints
    communication
    x cores == x processes?

    View Slide

  48. processes
    what about fork(2)
    and CoW friendly GC
    ?

    View Slide

  49. not CoW friendly

    View Slide

  50. threads
    shared state
    locks and lock granularity
    race conditions
    can be hard to reason about

    View Slide

  51. zomg! I love
    multithreaded code
    - NOBODY EVER

    View Slide

  52. View Slide

  53. View Slide

  54. so what’s up with
    MRI?

    View Slide

  55. well, nothing
    but...

    View Slide

  56. GIL

    View Slide

  57. some things the
    GIL is responsible for...

    View Slide

  58. San Francisco Bay oil spillage

    View Slide

  59. ...maritime disasters

    View Slide

  60. THANKS, GIL!

    View Slide

  61. GIL

    View Slide

  62. MRI
    not so bad if you’re
    I/O bound

    View Slide

  63. MRI
    but what about
    computation?

    View Slide

  64. meh.
    thread-level parallelism
    is available right now!
    ...just not with MRI

    View Slide

  65. View Slide

  66. rubinius 2.0.0
    due for release 2042
    OLD SLIDE ALERT!

    View Slide

  67. so now that we have
    truly parallel threads
    is the problem solved?

    View Slide

  68. !
    !
    rules of threading

    View Slide

  69. Don’t do it!

    View Slide

  70. If you must do it
    don’t share data
    across threads

    View Slide

  71. If you must share
    data across threads
    don’t share
    mutable data

    View Slide

  72. If you must share
    mutable data
    across threads
    synchronise access
    to this data

    View Slide

  73. don’t communicate by
    sharing memory...
    !
    ...share memory
    by communicating
    go

    View Slide

  74. View Slide

  75. painless
    multithreaded
    programming
    for ruby

    View Slide

  76. Tony Arcieri
    Tim Carey-Smith
    Ben Langfeld
    @bascule
    @halorgium
    @benlangfeld
    The Maintainers

    View Slide

  77. View Slide

  78. View Slide

  79. a concurrent object oriented
    programming framework which
    lets you build multithreaded
    programs out of concurrent
    objects just as easily as you
    build sequential programs
    out of regular objects

    View Slide

  80. View Slide

  81. based upon the
    actor model

    View Slide

  82. actor model
    first proposed
    way back in 1970

    View Slide

  83. actor model
    actors are isolated within
    lightweight processes

    View Slide

  84. actor model
    actors possess
    identity

    View Slide

  85. actor model
    absolutely
    no shared state

    View Slide

  86. actor model
    actors don’t need to
    compete for locks

    View Slide

  87. actor model
    are sent messages
    asynchronously

    View Slide

  88. actor model
    messages are
    buffered by a mailbox

    View Slide

  89. actor model
    the actor works off each
    message sequentially

    View Slide

  90. actor model
    has implementations
    in many languages

    View Slide

  91. View Slide

  92. View Slide

  93. View Slide

  94. View Slide

  95. celluloid actors
    automatically
    synchronize state

    View Slide

  96. 1 class Actor!
    2 attr_reader :counter!
    3 !
    4 def initialize!
    5 @counter = 0!
    6 @mutex = Mutex.new!
    7 end!
    8 !
    9 def increment!
    10 @mutex.synchronize do!
    11 @counter += 1!
    12 end!
    13 end!
    14 end!

    View Slide

  97. with celluloid
    the same example...

    View Slide

  98. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 attr_reader :counter!
    6 !
    7 def initialize!
    8 @counter = 0!
    9 end!
    10 !
    11 def increment!
    12 @counter += 1!
    13 end!
    14 end!

    View Slide

  99. View Slide

  100. celluloid actors
    are active objects
    living within threads

    View Slide

  101. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 end!
    6 !
    7 actor = Actor.new!
    8 actor.inspect!
    9 #=> !
    10 !
    11 Thread.main!
    12 #=> !
    13 !
    14 actor.thread!
    15 #=> !

    View Slide

  102. 1 module Celluloid!
    2 module ClassMethods!
    3 # Create a new actor!
    4 def new(*args, &block)!
    5 proxy = Actor.new(allocate, actor_options).proxy!
    6 proxy._send_(:initialize, *args, &block)!
    7 proxy!
    8 end!
    9 #...!
    10 end!
    11 #...!
    12 end!

    View Slide

  103. celluloid actors
    messages you send
    are buffered via the
    actor’s mailbox...

    View Slide

  104. celluloid actors
    ... until the actor is
    ready to act upon them

    View Slide

  105. ______________!
    < ETOOMANYACTS >!
    --------------!
    \ ^__^!
    \ (oo)\_______!
    (__)\ )\/\!
    ||----w |!
    || ||!
    !

    View Slide

  106. celluloid actors
    no pattern matching
    just regular messages

    View Slide

  107. celluloid actors
    poll their mailbox
    in a message loop

    View Slide

  108. 1 class Actor!
    2 # Wrap the given subject with an Actor!
    3 def initialize(subject, options = {})!
    4 @subject = subject!
    5 @mailbox = options[:mailbox] || Mailbox.new!
    6 @running = true!
    7 !
    8 @thread = ThreadHandle.new(:actor) do!
    9 setup_thread!
    10 run!
    11 end!
    12 #...!
    13 end!
    14 #...!
    15 end!

    View Slide

  109. 1 class Actor!
    2 def run!
    3 #...!
    4 while @running!
    5 if message = @mailbox.receive(timeout_interval)!
    6 handle_message message!
    7 else!
    8 # No message indicates a timeout!
    9 @timers.fire!
    10 @receivers.fire_timers!
    11 end!
    12 end!
    13 #...!
    14 shutdown!
    15 end!
    16 end!

    View Slide

  110. celluloid actors
    act upon messages
    sequentially

    View Slide

  111. what about ordering?
    no guarantees

    View Slide

  112. celluloid actors
    dispatch calls
    within fibers

    View Slide

  113. fibers?
    cooperative
    lightweight
    user space
    some gotchas...

    View Slide

  114. celluloid actors
    can dispatch
    synchronously

    View Slide

  115. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 sleep 2!
    8 puts "42"!
    9 end!
    10 end!
    11 !
    12 actor = Actor.new!
    13 actor.compute_all_the_things!
    14 puts "done!"!
    !
    #=> 42!
    #=> done!!
    blocking

    View Slide

  116. celluloid actors
    can dispatch
    asynchronously

    View Slide

  117. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 sleep 2!
    8 puts "42"!
    9 end!
    10 end!
    11 !
    12 actor = Actor.new!
    13 actor.async.compute_all_the_things!
    14 puts "done!"!
    15 !
    16 #=> done!!
    17 #=> 42!
    returns
    immediately

    View Slide

  118. celluloid actors
    can perform tasks
    in futures

    View Slide

  119. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 sleep 2!
    8 "42"!
    9 end!
    10 end!
    11 !
    12 actor = Actor.new!
    13 future = actor.future.compute_all_the_things!
    14 puts "done!"!
    15 puts future.value!
    16 !
    17 #=> done!!
    18 #=> 42!
    returns immediately
    blocks until a
    value is yielded

    View Slide

  120. celluloid actors
    are accessible by
    reference or name

    View Slide

  121. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 sleep 2!
    8 puts "42"!
    9 end!
    10 end!
    11 !
    12 actor = Actor.new!
    13 Celluloid::Actor[:foo] = actor!
    14 !
    15 actor.inspect!
    16 #=> !
    17 Celluloid::Actor[:foo].inspect!
    18 #=> !

    View Slide

  122. celluloid actors
    are fault tolerant
    ... let it crash!

    View Slide

  123. 1 require "celluloid/autostart"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 puts "42"!
    8 end!
    9 !
    10 def zomg_crash!
    11 raise "derp!"!
    12 end!
    13 end!
    14 !
    15 supervisor = Actor.supervise_as :foo!
    16 !
    17 begin!
    18 Celluloid::Actor[:foo].zomg_crash!
    19 rescue!
    20 puts "whoops"!
    21 end!
    22 !
    23 Celluloid::Actor[:foo].compute_all_the_things!
    24 !
    25 #=> whoops!
    26 #=> 42!
    crash the actor
    fresh actor
    take care of me!

    View Slide

  124. celluloid actors
    can be arranged
    as pooled workers

    View Slide

  125. 1 require "celluloid"!
    2 !
    3 class Actor!
    4 include Celluloid!
    5 !
    6 def compute_all_the_things!
    7 sleep 1!
    8 puts "42"!
    9 end!
    10 end!
    11 !
    12 pool = Actor.pool!
    13 !
    14 4.times { pool.compute_all_the_things }!
    15 !
    16 #=> 42!
    17 #=> 42 and so on...!
    size*cores
    load up the
    workers

    View Slide

  126. there’s more
    timers
    links
    supervision groups
    pub/sub
    conditions

    View Slide

  127. that low hanging fruit?
    yeah, about that...

    View Slide

  128. but there is one tip!
    blocking I/O...
    don’t

    View Slide

  129. View Slide

  130. an event-driven IO system for
    building fast, scalable network
    applications that integrate
    directly with celluloid actors

    View Slide

  131. View Slide

  132. unlike certain other evented
    I/O systems which limit you to a
    single event loop per process
    Celluloid::IO lets you make
    as many actors as you want
    system resources permitting

    View Slide

  133. View Slide

  134. View Slide

  135. a distributed extension to
    celluloid which provides
    distributed and concurrent
    objects for ruby that are both
    robust and fault-tolerant

    View Slide

  136. View Slide

  137. a fast non-blocking and
    evented web server. Thanks to
    celluloid, Reel works great for
    multithreaded applications and
    provides traditional
    multithreaded blocking IO
    support too.

    View Slide

  138. EXPERIMENTAL
    or broken as it is known outside of OSS

    View Slide

  139. to summarise...

    View Slide

  140. the future of ruby
    concurrency
    and parallelism?

    View Slide

  141. thanks!
    @benlovell
    ?

    View Slide