[ACCEPTED]-Does ruby have real multithreading?-concurrency
Updated with Jörg's Sept 2011 comment
You seem to be confusing two very different 119 things here: the Ruby Programming Language 118 and the specific threading model of one 117 specific implementation of the Ruby Programming 116 Language. There are currently around 11 115 different implementations of the Ruby Programming 114 Language, with very different and unique threading 113 models.
(Unfortunately, only two of those 112 11 implementations are actually ready for 111 production use, but by the end of the year 110 that number will probably go up to four 109 or five.) (Update: it's now 5: MRI, JRuby, YARV 108 (the interpreter for Ruby 1.9), Rubinius 107 and IronRuby).
The first implementation doesn't 106 actually have a name, which makes it quite 105 awkward to refer to it and is really annoying 104 and confusing. It is most often referred 103 to as "Ruby", which is even more 102 annoying and confusing than having no name, because 101 it leads to endless confusion between the 100 features of the Ruby Programming Language 99 and a particular Ruby Implementation.
It 98 is also sometimes called "MRI" (for 97 "Matz's Ruby Implementation"), CRuby 96 or MatzRuby.
MRI implements Ruby Threads as Green Threads within its interpreter. Unfortunately, it doesn't 95 allow those threads to be scheduled in 94 parallel, they can only run one thread at 93 a time.
However, any number of C Threads 92 (POSIX Threads etc.) can run in parallel 91 to the Ruby Thread, so external C Libraries, or 90 MRI C Extensions that create threads of 89 their own can still run in parallel.
The 88 second implementation is YARV (short for "Yet 87 Another Ruby VM"). YARV implements Ruby Threads as POSIX or Windows NT Threads, however, it uses 86 a Global Interpreter Lock (GIL) to ensure 85 that only one Ruby Thread can actually be 84 scheduled at any one time.
Like MRI, C Threads 83 can actually run parallel to Ruby Threads.
In 82 the future, it is possible, that the GIL 81 might get broken down into more fine-grained 80 locks, thus allowing more and more code 79 to actually run in parallel, but that's 78 so far away, it is not even planned yet.
JRuby implements Ruby Threads as Native Threads, where 77 "Native Threads" in case of the 76 JVM obviously means "JVM Threads". JRuby 75 imposes no additional locking on them. So, whether 74 those threads can actually run in parallel 73 depends on the JVM: some JVMs implement 72 JVM Threads as OS Threads and some as Green 71 Threads. (The mainstream JVMs from Sun/Oracle 70 use exclusively OS threads since JDK 1.3)
XRuby also 69 implements Ruby Threads as JVM Threads. Update: XRuby is dead.
IronRuby implements Ruby Threads as Native Threads, where "Native 68 Threads" in case of the CLR obviously 67 means "CLR Threads". IronRuby 66 imposes no additional locking on them, so, they 65 should run in parallel, as long as your 64 CLR supports that.
Ruby.NET also implements Ruby Threads as CLR Threads. Update: Ruby.NET is 63 dead.
Rubinius implements Ruby Threads as Green Threads within its Virtual Machine. More precisely: the Rubinius VM 62 exports a very lightweight, very flexible 61 concurrency/parallelism/non-local control-flow 60 construct, called a "Task", and all 59 other concurrency constructs (Threads in 58 this discussion, but also Continuations, Actors and other 57 stuff) are implemented in pure Ruby, using 56 Tasks.
Rubinius can not (currently) schedule 55 Threads in parallel, however, adding that 54 isn't too much of a problem: Rubinius can 53 already run several VM instances in several POSIX Threads in parallel, within one Rubinius process. Since 52 Threads are actually implemented in Ruby, they 51 can, like any other Ruby object, be serialized 50 and sent to a different VM in a different 49 POSIX Thread. (That's the same model the 48 BEAM Erlang VM uses for SMP concurrency. It is 47 already implemented for Rubinius Actors.)
Update: The information about Rubinius 46 in this answer is about the Shotgun VM, which 45 doesn't exist anymore. The "new" C++ VM 44 does not use green threads scheduled across 43 multiple VMs (i.e. Erlang/BEAM style), it 42 uses a more traditional single VM with multiple 41 native OS threads model, just like the one 40 employed by, say, the CLR, Mono, and pretty 39 much every JVM.
MacRuby started out as a port of 38 YARV on top of the Objective-C Runtime 37 and CoreFoundation and Cocoa Frameworks. It 36 has now significantly diverged from YARV, but 35 AFAIK it currently still shares the same Threading Model with YARV. Update: MacRuby depends 34 on apples garbage collector which is declared 33 deprecated and will be removed in later 32 versions of MacOSX, MacRuby is undead.
Cardinal is 31 a Ruby Implementation for the Parrot Virtual Machine. It doesn't 30 implement threads yet, however, when it 29 does, it will probably implement them as 28 Parrot Threads. Update: Cardinal seems very inactive/dead.
MagLev is 27 a Ruby Implementation for the GemStone/S Smalltalk VM. I have no 26 information what threading model GemStone/S 25 uses, what threading model MagLev uses or 24 even if threads are even implemented yet 23 (probably not).
HotRuby is not a full Ruby Implementation 22 of its own. It is an implementation of 21 a YARV bytecode VM in JavaScript. HotRuby 20 doesn't support threads (yet?) and when 19 it does, they won't be able to run in parallel, because 18 JavaScript has no support for true parallelism. There 17 is an ActionScript version of HotRuby, however, and 16 ActionScript might actually support parallelism. Update: HotRuby 15 is dead.
Unfortunately, only two of these 14 11 Ruby Implementations are actually production-ready: MRI 13 and JRuby.
So, if you want true parallel 12 threads, JRuby is currently your only choice – not 11 that that's a bad one: JRuby is actually 10 faster than MRI, and arguably more stable.
Otherwise, the 9 "classical" Ruby solution is to 8 use processes
instead of threads for parallelism. The 7 Ruby Core Library
contains the Process
module with the 6 Process.fork
method which makes it dead easy to fork off another 5 Ruby
process. Also, the Ruby Standard Library 4 contains the
Distributed Ruby (dRuby / dRb) library, which allows Ruby 3
code to be trivially distributed across 2 multiple processes, not
only on the same 1 machine but also across the network.
Ruby 1.8 only has green threads, there is 10 no way to create a real "OS-level" thread. But, ruby 9 1.9 will have a new feature called fibers, which 8 will allow you to create actual OS-level 7 threads. Unfortunately, Ruby 1.9 is still 6 in beta, it is scheduled to be stable in 5 a couple of months.
Another alternative is 4 to use JRuby. JRuby implements threads 3 as OS-level theads, there are no "green 2 threads" in it. The latest version of JRuby 1 is 1.1.4 and is equivalent to Ruby 1.8
It depends on the implementation:
- MRI doesn't have, YARV is closer.
- JRuby and MacRuby have.
Ruby 25 has closures as Blocks
, lambdas
and Procs
. To take full advantage 24 of closures and multiple cores in JRuby, Java's executors come 23 in handy; for MacRuby I like GCD's queues.
Note that, being 22 able to create real "OS-level" threads doesn't imply that you can 21 use multiple cpu cores for parallel processing. Look 20 at the examples below.
This is the output 19 of a simple Ruby program which uses 3 threads using Ruby 2.1.0:
(jalcazar@mac ~)$ ps -M 69877
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 69877 s002 0.0 S 31T 0:00.01 0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
69877 0.0 S 31T 0:00.01 0:00.00
69877 33.4 S 31T 0:00.01 0:08.73
69877 43.1 S 31T 0:00.01 0:08.73
69877 22.8 R 31T 0:00.01 0:08.65
As you can see here, there 18 are four OS threads, however only the one 17 with state R
is running. This is due to a 16 limitation in how Ruby's threads are implemented.
Same 15 program, now with JRuby. You can see three 14 threads with state R
, which means they are 13 running in parallel.
(jalcazar@mac ~)$ ps -M 72286
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 72286 s002 0.0 S 31T 0:00.01 0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 33T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.09 0:02.34
72286 7.9 S 31T 0:00.15 0:04.63
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.04 0:01.68
72286 0.0 S 31T 0:00.03 0:01.54
72286 0.0 S 31T 0:00.00 0:00.00
72286 0.0 S 31T 0:00.01 0:00.01
72286 0.0 S 31T 0:00.00 0:00.01
72286 0.0 S 31T 0:00.00 0:00.03
72286 74.2 R 31T 0:09.21 0:37.73
72286 72.4 R 31T 0:09.24 0:37.71
72286 74.7 R 31T 0:09.24 0:37.80
The same program, now 12 with MacRuby. There are also three threads 11 running in parallel. This is because MacRuby threads are POSIX threads (real "OS-level" threads) and 10 there is no GVL
(jalcazar@mac ~)$ ps -M 38293
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 38293 s002 0.0 R 0T 0:00.02 0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
38293 0.0 S 33T 0:00.00 0:00.00
38293 100.0 R 31T 0:00.04 0:21.92
38293 100.0 R 31T 0:00.04 0:21.95
38293 100.0 R 31T 0:00.04 0:21.99
Once again, the same program but 9 now with the good old MRI. Due to the fact 8 that this implementation uses green-threads, only 7 one thread shows up
(jalcazar@mac ~)$ ps -M 70032
USER PID TT %CPU STAT PRI STIME UTIME COMMAND
jalcazar 70032 s002 100.0 R 31T 0:00.08 0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb
If you are interested 6 in Ruby multi-threading you might find my 5 report Debugging parallel programs using fork handlers interesting.
For a more general 4 overview of the Ruby internals Ruby Under a Microscope is a good 3 read.
Also, Ruby Threads and the Global Interpreter Lock in C in Omniref explains in the 2 source code why Ruby threads don't run in 1 parallel.
How about using drb? It's not real multi-threading 3 but communication between several processes, but 2 you can use it now in 1.8 and it's fairly 1 low friction.
I'll let the "System Monitor" answer this 12 question. I'm executing the same code (below, which 11 calculates prime numbers) with 8 Ruby threads 10 running on an i7 (4 hyperthreaded-core) machine 9 in both cases... the first run is with:
jruby 8 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 7 6586) (OpenJDK 64-Bit Server VM 1.7.0_75) [amd64-java]
The 6 second is with:
ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]
Interestingly, the 5 CPU is higher for JRuby threads, but the 4 time to completion is slightly shorter for 3 the interpreted Ruby. It's kind of difficult 2 to tell from the graph, but the second (interpreted 1 Ruby) run uses about 1/2 the CPUs (no hyperthreading?)
def eratosthenes(n)
nums = [nil, nil, *2..n]
(2..Math.sqrt(n)).each do |i|
(i**2..n).step(i){|m| nums[m] = nil} if nums[i]
end
nums.compact
end
MAX_PRIME=10000000
THREADS=8
threads = []
1.upto(THREADS) do |num|
puts "Starting thread #{num}"
threads[num]=Thread.new { eratosthenes MAX_PRIME }
end
1.upto(THREADS) do |num|
threads[num].join
end
If you are using MRI, then you can write 2 the threaded code in C either as an extension 1 or using the ruby-inline gem.
If you really need parallelism in Ruby for 6 a Production level system (where you cannot 5 employ a beta) processes are probably a 4 better alternative.
But, it is most definitely 3 worth trying threads under JRuby first.
Also 2 if you are interested in future of threading 1 under Ruby, you might find this article useful.
Here is some info on Rinda which is Ruby 2 implementation of Linda (parallel processing 1 and distributed computing paradigm) http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html
Because could not edit that answer, so add 19 a new reply here.
Update(2017-05-08)
This article is very old, and 18 information is not follow current (2017) tread, Following 17 is some supplement:
Opal is a Ruby to JavaScript 16 source-to-source compiler. It also has an 15 implementation of the Ruby corelib, It current 14 very active develompent, and exist a great 13 deal of (frontend) framework worked on it. and 12 production ready. Because base on javascript, it 11 not support parallel threads.
truffleruby is a high 10 performance implementation of the Ruby programming 9 language. Built on the GraalVM by Oracle 8 Labs,TruffleRuby is a fork of JRuby, combining 7 it with code from the Rubinius project, and 6 also containing code from the standard implementation 5 of Ruby, MRI, still live development, not 4 production ready. This version ruby seem 3 like born for performance, I don't know 2 if support parallel threads, but I think 1 it should.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.