Concurrency

&
Ruby





Rocky Jaiswal
RubyConf India 2013

WhY Concurrency?

About Me

Learning programming for the last 11 years
Did Java for around 8 years

Started learning Ruby ~3 years back

Ruby
the Ruby community

Also learning some CoffeeScript and Scala

http://rockyj.in
@whatsuprocky

Concurrency?

Concurrency is when two tasks can start, run, and complete in overlapping time periods

Concurrency can be implemented even in single processing units to speed things up

Concurrency is non-deterministic

Whereas a  parallel program is one that merely runs on multiple processors, with the goal of hopefully running faster than it would on a single CPU

THREADS vs Processess



Threads are light weight processes that run in the same memory context

Ruby has Green Threads which are managed by the Ruby process

JRuby has real OS thread that run parallel to the parent thread

Threads in Ruby


Sample Unicorn Setup


15 Unicorns = 15 Processes
1 Unicorn Process ~= 150 MB
15 Processes ~= 2 GB RAM*

Scaling this means more processes = more memory = more money

Also, If you are CPU bound you want to use no more unicorn processes than you have cores, otherwise you overload the system and slow down the scheduler.

Concurrency is good


JRuby + Puma / Torquebox

High-Scalability with less memory



Resque / Sidekiq

More workers and faster processing with less memory

SO is it all doom and gloom?


No!

  • Most Rails applications are IO bound
  • With MRI you are always thread safe
  • MRI is getting faster and GC is getting better
  • Processes management is optimized
  • Passenger is using a hybrid - evented + threaded / process architecture

Thread-Safety

Let me give you a demo


Appending to Arrays:

MRI Version
vs
JRuby Version

DEMO


RUN CODE on MRI & JRuby


array = []
5.times.map do
  Thread.new do #Init 5 threads
    1000.times do
      array << nil #In each thread add 1000 elements to the Array
    end
  end
end.each(&:join)
puts array.size

Even APPENDING TO ARRAYS IS NOT THREAD SAFE!



What About Rails




config.threadsafe!

 def threadsafe!
  @preload_frameworks = true
  @cache_classes      = true
  @dependency_loading = false
  @allow_concurrency  = true
  self
end

JRUBY ON RAILS




DEMO

BAD COUNTER CODE


 class PagesController < ApplicationController
  @counter = 0
  class << self
    attr_accessor :counter
  end
  #Classic read-modify-write problem
  def index
    counter = self.class.counter # read
    sleep(0.1)
    counter += 1 #update
    sleep(0.1)
    self.class.counter = counter # write
    users = User.all
    puts "-----------" + self.class.counter.to_s + "------------"
  end
end

UGLY SYNCHRONIZED CODE


 class PagesController < ApplicationController
  @counter = 0
  @semaphore = Mutex.new
  class << self
    attr_accessor :counter
    attr_accessor :semaphore
  end
  def index
    #counter = self.class.counter # read
    sleep(0.1)
    self.class.semaphore.synchronize {
      self.class.counter += 1 #update
    }
    sleep(0.1)
    #self.class.counter = counter # write
    users = User.all
    puts "-----------" + self.class.counter.to_s + "------------"
  end
end

Rails 4 is concurrency enabled by default





concurrency introduces


Race Conditions
Deadlocks
Starvation
etc.

But gives you

Speed
Less Memory Usage

Safe concurrency



  • Don't do it.
  • If you must do it, don't share data across threads.
  • If you must share data across threads, don't share mutable data.
  • If you must share mutable data across threads, synchronize access to that data.

Thread Safety in JRuby


Locks


ATOMICITY


IMMUTABILITY



ATOMIC Counter


java_import 'java.util.concurrent.atomic.AtomicInteger'

class PagesController < ApplicationController
  @counter = AtomicInteger.new(1) 
  
  class << self
    attr_accessor :counter
  end

  def index
    sleep(0.1)
    counter = self.class.counter.getAndIncrement() #update
    sleep(0.1)
    users = User.all
    puts "-----------------" + counter.to_s + "-----------------"
  end
end

All this sucks!


95% of syncronized code is broken. The other 5% is written by Brian Goetz. - Venkat Subramaniam

Enter Actor



The Actor Model


Introduced by Carl Hewitt in 1973
Contributions by a lot of scholars and universities
Popularized by Erlang, now in Scala

  • Simple and high-level abstractions for concurrency and parallelism
  • Objects are Actors each with their own state which is never shared
  • Communication happens through messages
  • Very lightweight event-driven processes (approximately 2.7 million actors per GB RAM [Akka])

THE ACTOR MODEL -2


Easier to deal with humans than with threads

Like humans, Actors communicate via messages

No state sharing, communicate via immutable messages

Implementations





Producer Consumer problem



Demo with JRuby + Locks
Demo with JRuby + Celluloid

Producer consumer

with locks



https://gist.github.com/rocky-jaiswal/5847810

Producer Consumer

with actors



HTTPS://GIST.GITHUB.COM/ROCKY-JAISWAL/5847814

Summary


  • Concurrency is the need of the hour
  • MRI is thread safe by default due to GIL / GVL
  • JRuby gives you real concurrency (RBX as well)
  • With power comes responsibility
  • Don't worry, concurrency can be easy if you follow the ground rules
  • If you want to write concurrent code yourself, use Actors

* I did not cover STM (provided by Clojure)

Thank you!





Questions




#A lot of this content has been taken from blogs, wikis and books. I do not claim it is my own and I wholeheartedly thank everyone who helped me with this presentation.

ConcurrencyinRuby

By rockyj

ConcurrencyinRuby

  • 919