Potential and Weakness Analysis of Ruby multithreading, and ruby Multithreading
Most Web applications are IO-intensive. Using the Ruby multi-process + multi-thread model can greatly increase the system throughput. The reason is: when a Ruby thread is in the IO Block State, other threads can continue to execute. However, due to the existence of Ruby GIL (Global Interpreter Lock), MRI Ruby does not really use multithreading for parallel computing. In addition to GIL, JRuby is a true multi-thread. It can cope with IO blocks and make full use of multi-core CPUs to speed up the overall operation.
The above is abstract. The following examples are used to describe them one by one.
Ruby multithreading and IO Block
First look at the following code (demonstration purpose, no practical use ):
Copy codeThe Code is as follows:
# 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 very simple. Three methods use sleep to simulate time-consuming IO operations. The result of running the code (Environment MRI Ruby 1.9.3) is:
Copy codeThe Code is as follows:
$ Time ruby block_io1.rb
Sleep 3 seconds in func1
Sleep 2 seconds in func2
Sleep 5 seconds in func3
Real 0m11. 681 s
User 0m3. 086 s
Sys 0m0. 152 s
The process is slow, and the time is spent on sleep. It takes more than 10 seconds in total.
Multithreading is adopted. Rewrite is as follows:
Copy codeThe Code is 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. join}
The running result is:
Copy codeThe Code is as follows:
$ Time ruby block_io2.rb
Sleep 3 seconds in func1
Sleep 2 seconds in func2
Sleep 5 seconds in func3
Real 0m6. 543 s
User 0m3. 169 s
Sys 0m0. 147 s
It took more than 6 seconds in total, obviously much faster, just a little more than the longest sleep 5 seconds.
The preceding example shows that multiple threads in Ruby can cope with IO blocks. When a thread is in the I/O Block State, other threads can continue to execute, greatly shortening the overall processing time.
Ruby GIL impact
Let's take a look at a piece of code (demonstration purpose ):
Copy codeThe Code is as follows:
# File: gil1.rb
Require 'securerandom'
Require 'zlib'
Data = SecureRandom. hex (4096000)
16. times {Zlib: Deflate. deflate (data )}
The code first generates some random data and then compresses it. compression is very CPU-consuming. The running results on my machine (dual-core CPU, MRI Ruby 1.9.3) are as follows:
Copy codeThe Code is as follows:
$ Time ruby gil1.rb
Real 0m8. 572 s
User 0m8. 359 s
Sys 0m0. 102 s
The Code is as follows:
Copy codeThe Code is as follows:
# File: gil2.rb
Require 'securerandom'
Require 'zlib'
Data = SecureRandom. hex (4096000)
Threads = []
16. times do
Threads <Thread. new {Zlib: Deflate. deflate (data )}
End
Threads. each {| t. join}
The results of multi-threaded version running are as follows:
Copy codeThe Code is as follows:
$ Time ruby gil2.rb
Real 0m8. 616 s
User 0m8. 377 s
Sys 0m0. 211 s
From the results, we can see that due to the presence of MRI Ruby GIL, multiple threads in Ruby cannot reuse multi-core CPUs. The overall time spent after multithreading is not shortened, but due to the impact of thread switching, the amount of time has increased slightly.
Remove GIL from JRuby
Run gil1.rb and gil2.rb on JRuby (my machine is JRuby 1.7.0) and get different results.
Copy codeThe Code is as follows:
$ Time jruby gil1.rb
Real 0 M12. 225 s
User 0m14. 060 s
Sys 0m0. 615 s
Copy codeThe Code is as follows:
$ Time jruby gil2.rb
Real 0m7. 584 s
User 0m22. 822 s
Sys 0m0. 819 s
We can see that the overall running time of JRuby is significantly shortened (7.58 to 12.22) When multithreading is used. This is because JRuby removes GIL and can truly execute multiple threads in parallel, making full use of the multi-core CPU.
Conclusion: Ruby multithreading can still execute other threads in a thread IO Block to reduce the overall impact of IO Block. However, due to the presence of MRI Ruby GIL, MRI Ruby is not a real parallel execution. Without GIL, JRuby can achieve real multi-thread parallel execution.