Igor Alexandrov

Programming and something else…

Celluloid – Ruby Multithreading Made Easy

| Comments

Today I discovered Celluloid. To be short, with Celluloid multithreading in Ruby is getting easy and understandable, it helps you to combine concurrency with object oriented programming.

Celluloid frees you from many worries and problems, that you have when you write multithreaded program. Here are some of its features:

  • automatic “deadlock-free” synchronization;
  • fault-tolerance;
  • ability to call a method “in the background”.

I don’t have much time now to write full review, so I will give you just a small example that illustrates how Celluloid works.

celluloid-test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
require 'rubygems'
require 'celluloid'
require 'open-uri'
require 'cgi'
require 'benchmark'

module Enumerable
    def pmap(&block)
        futures = map { |elem| Celluloid::Future.new(elem, &block) }
        futures.map { |future| future.value }
    end
end

def image_url(color, term)
    url = "http://www.google.com/search?q=#{term}&tbm=isch&tbs=ic:specific,isc:#{color}"
    page = open(url) { |sock| sock.read }
    CGI.unescape page.match(/imgurl=(.+?)&/)[1]
end

term = 'dog'
colors = %w(red green yellow blue black brown)

n = 20

Benchmark.bm(7) do |x|
    x.report("map:")   {
        n.times do
        colors.map { |color| image_url(color, term) }
        end
    }

    x.report("pmap:") {
        n.times do
        colors.pmap { |color| image_url(color, term) }
        end
    }
end

Results

ruby-1.9.3-p327

1
2
3
4
5
6
7
8
9
saturn:~/workspace$ ruby --version
ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-darwin12.2.0]
saturn:~/workspace$ ruby celluloid_test.rb
            user     system      total        real
map:      0.920000   0.190000   1.110000 ( 28.897125)
pmap:     0.790000   0.170000   0.960000 (  5.543667)
I, [2012-12-09T23:22:29.793886 #51073]  INFO -- : Terminating 4 actors...
I, [2012-12-09T23:22:29.794885 #51073]  INFO -- : Shutdown completed cleanly
saturn:~/workspace$ 

jruby 1.7.0

1
2
3
4
5
6
7
8
saturn:~/workspace$ ruby --version
jruby 1.7.0 (1.9.3p203) 2012-10-22 ff1ebbe on Java HotSpot(TM) 64-Bit Server VM 1.7.0_09-b05 [darwin-x86_64]
saturn:~/workspace$ ruby celluloid_test.rb
            user     system      total        real
map:      2.580000   0.260000   2.840000 ( 29.473000)
pmap:     2.820000   0.230000   3.050000 (  5.581000)
I, [2012-12-10T00:06:06.377000 #52153]  INFO -- : Terminating 7 actors...
I, [2012-12-10T00:06:06.379000 #52153]  INFO -- : Shutdown completed cleanly

ruby 2.0.0-preview1

1
2
3
4
5
6
7
8
saturn:~/workspace$ ruby --version
ruby 2.0.0dev (2012-11-01 trunk 37411) [x86_64-darwin12.2.0]
saturn:~/workspace$ ruby celluloid_test.rb
            user     system      total        real
map:      0.540000   0.180000   0.720000 ( 29.701226)
pmap:     0.440000   0.190000   0.630000 (  5.488692)
I, [2012-12-10T00:09:59.811204 #54194]  INFO -- : Terminating 4 actors...
I, [2012-12-10T00:09:59.811898 #54194]  INFO -- : Shutdown completed cleanly

Looks promising, yes? For me too! I am trying to make XML import process for more then t 200000 objects faster with this library.

Comments