Web applications are mostly IO-intensive, and using Ruby's multi-process + multithreaded model will greatly increase system throughput. The reason for this is that when a ruby thread is in the IO block state, other threads can continue to execute. But because of Ruby GIL (Global interpreter Lock), MRI Ruby does not really use multithreading for parallel computing. JRuby removed the GIL, which is a true multi-threaded, capable of both dealing with IO blocks and making full use of multi-core CPUs to speed up the overall operation.
The above is more abstract, the following is illustrated with examples.
Ruby Multithreading and IO block
Look at the following code (for demonstration purposes, no actual use):
# FILE:BLOCK_IO1.RB
def func1
Puts "sleep 3 seconds in func1\n"
Sleep (3)
End
def FUNC2
Puts "Sleep 2 seconds in func2\n"
Sleep (2)
End
def func3
Puts "sleep 5 seconds in func3\n"
Sleep (5)
End
Func1
Func2
Func3
The code is simple, 3 ways to simulate a time-consuming IO operation with sleep. The results of the Run code (Environment MRI Ruby 1.9.3) are:
$ Time Ruby Block_io1.rb
Sleep 3 seconds in func1
Sleep 2 seconds in Func2
Sleep 5 seconds in func3
Real 0m11.681s
User 0m3.086s
SYS 0m0.152s
Slow, time spent on sleep, a total of more than 10 seconds.
In a multi-threaded manner, rewrite as follows:
# FILE:BLOCK_IO2.RB
def func1
Puts "sleep 3 seconds in func1\n"
Sleep (3)
End
def FUNC2
Puts "Sleep 2 seconds in func2\n"
Sleep (2)
End
def func3
Puts "sleep 5 seconds in func3\n"
Sleep (5)
End
Threads = []
Threads << thread.new {func1}
Threads << thread.new {FUNC2}
Threads << thread.new {func3}
Threads.each {|t| T.join}
The results of the operation are:
$ Time Ruby Block_io2.rb
Sleep 3 seconds in func1
Sleep 2 seconds in Func2
Sleep 5 seconds in func3
Real 0m6.543s
User 0m3.169s
SYS 0m0.147s
It took a total of 6 seconds, significantly faster, and only a little more than the longest sleep 5 seconds.
The above example shows that Ruby's multithreading is capable of dealing with IO block, and that when a thread is in IO block state, other threads can continue to perform, which shortens the overall processing time.
the impact of Ruby GIL
Or look at a piece of code first (demo purpose):
# FILE:GIL1.RB
Require ' securerandom '
Require ' zlib '
data = Securerandom.hex (4096000)
16.times {Zlib::D eflate.deflate (data)}
The code first randomly generates some data, then compresses it, the compression is very CPU-intensive, and the results of my machine (dual-core CPU, MRI Ruby 1.9.3) run as follows:
$ Time Ruby Gil1.rb
Real 0m8.572s
User 0m8.359s
SYS 0m0.102s
To a multithreaded version, the code is as follows:
# FILE:GIL2.RB
Require ' securerandom '
Require ' zlib '
data = Securerandom.hex (4096000)
Threads = []
16.times do
Threads << thread.new {Zlib::D eflate.deflate (data)}
End
Threads.each {|t| T.join}
The results of a multithreaded version run are as follows:
$ Time Ruby Gil2.rb
Real 0m8.616s
User 0m8.377s
SYS 0m0.211s
As can be seen from the results, because of the presence of the MRI Ruby GIL, Ruby multithreading does not re-use multi-core CPUs, the overall time spent using multiple threads is not shortened, but due to the impact of thread switching, the time spent slightly increased.
JRuby removed GIL.
Using JRuby (my machine is JRuby 1.7.0) runs gil1.rb and gil2.rb and gets very different results.
$ Time JRuby gil1.rb
Real 0m12.225s
User 0m14.060s
SYS 0m0.615s
$ Time JRuby gil2.rb
Real 0m7.584s
User 0m22.822s
SYS 0m0.819s
As you can see, when JRuby uses multiple threads, the overall uptime is significantly shortened (7.58:12.22), because JRuby removes the GIL and can actually execute multiple threads in parallel, leveraging multi-core CPUs.
Summary: Ruby multithreading can still execute other threads while at one thread IO block, thus reducing the overall impact of Io block, but because of the presence of the MRI Ruby GIL, MRI Ruby is not really parallel execution, JRuby removed the GIL and can do real A positive multithreaded parallel execution.