Actors and Threads Safer Concurrency for Ruby Mike Perham @mperham
Who Am I?• Numerous gems (dalli, memcache-client, rack-fiber_pool, connection_pool, etc)• Scalability/Performance guy• Fibers and EventMachine• Technical Lead at Carbon Five
What This Isn’t• Threads vs Events vs Processes• Threads won’t make you love Java
Definitions• Concurrency - performing two operations in tandem (e.g. two web requests)• Parallelism - performing two operations literally at the same time (requires two cores) Concurrent Parallel
Concurrency• Processes• Threads
The problem is always communication (true in the real world too!)
Communication• Two fundamental mechanisms • Share • Copy
Threads• Much more efficient than processes• Communication mechanism? • Variables!• POSIX 1995
Threads
Threads
End Goals• Runtime Efficiency isn’t the only goal!• Remember Fun?• Ease of Development is huge
Locks• Hard to get right• Non-deterministic• Don’t scale
Locks
Lock Benchmark# Results: Ruby 1.9.2, GIL means no parallel threading# user system total real# single locked 4.310000 0.010000 4.320000 ( 4.311850)# threaded locked 4.230000 0.080000 4.310000 ( 4.307405)## Results: JRuby 1.6.3, parallel threads mean massive lock contention# user system total real# single locked 4.080000 0.000000 4.080000 ( 4.080000)# threaded locked 17.109000 0.000000 17.109000 ( 17.109000) Context Switching!
Most Important Slide!
What can we do?• Look to other languages • Go • Scala • Erlang
Goroutines• Asynchronous function • You cannot get a handle to it • Send messages via a channel
Goroutines• Really like this model• Maps well to a distributed model• Backed by a pool of threads
Actors• unit of execution• has a mailbox• You just send it a message, e.g. • actor << { :amount => 11.99 }
Actors• Thread- or Fiber-backed• No application-level locks
Actor.rb• MRI/JRuby - no actors• Rubinius comes with actor.rb
Actor.rb
Actor.rb• Not idiomatic• Hard to understand, use correctly
Celluloid• Tony Arcieri (Revactor, Reia)• https://github.com/tarcieri/celluloid• OO Actors• Asynchronous method invocation• Mix of Threads and Fibers
Celluloid Example
Celluloid Thoughts• Nice, idiomatic Ruby design• One object == one thread• Needs actor pooling
girl_friday• yours truly• http://github.com/mperham/girl_friday• Background processing pools• Parallel batch operations• More functional than OO• Thread-based
girl_friday example
girl_friday example
What if we do need to share data?
I Love the 80s!
I Love the 00s!
STM• Software Transactional Memory• All mutation via transactions• Provide ACI (not ACID) guarantees• See Clojure (and Cloby!)
Atomic Instructions• Since Pentium (1993)• XCHG - swap registers or register/ memory• CPMXCHG - Compare And Set (‘CAS’)
Atomic!
Atomic Benchmark# Results: JRuby 1.6.3# user system total real# single atomic 2.041000 0.000000 2.041000 ( 2.041000)# threaded atomic 0.978000 0.000000 0.978000 ( 0.979000) 2 cores = 2x speedup!