Ruby で Thread を使う(その1)
suetotanuki さんに触発されて、Ruby + Thread を試してみました。
http://sutetotanuki.hatenablog.com/entry/2013/05/12/201534
以下のあたりの資料が、すごく参考になりました。
CRubyのロックデザインの解説および改善案について
http://rubykaigi.org/2011/ja/schedule/details/17S10
#vimeo は必見ですね。
第19章 スレッド
http://i.loveruby.net/ja/rhg/book/thread.html
#このあたりの資料も、軽く読みました。
何となくですが、今の実装だと複数コアに股がった
並列処理は出来そうになさそう。
RubyのMVM APIの共同研究が始まる
http://www.infoq.com/jp/news/2008/03/ruby-mvm-research
#こんな記事もあるし、
#いずれ、VM が並列で走行できる日が来るかもしれませんが、、、
suetotanuki さんのソースコードを、CoreDuo Macbook で走行させてみました。
#理由は、Core i5 の Macbook Air だとCPU Usage が100% 超えてたから。
#多分、HyperThreading のせいだと思うんだけど、HT OFF の方法がわからん
$ ruby -v ruby 1.9.3p392 (2013-02-22 revision 39386) [i386-darwin10.8.0] $ cat tp.rb def fib(n) n < 2 ? n : fib(n-1) + fib(n - 2) end th1 = Thread.new { sleep 1 100.times { fib(1000) } } th2 = Thread.new { sleep 1 100.times { fib(1000) } } th1.join; th2.join; $ ruby ./tp.rb & [1] 329 $ top -pid 329 ...
Processes: 61 total, 3 running, 58 sleeping, 250 threads 23:34:39
Load Avg: 0.80, 0.40, 0.20 CPU usage: 58.25% user, 4.58% sys, 37.15% idle
SharedLibs: 180M resident, 5008K data, 35M linkedit.
MemRegions: 6110 total, 295M resident, 17M private, 201M shared.
PhysMem: 207M wired, 434M active, 102M inactive, 743M used, 1303M free.
VM: 42G vsize, 460M framework vsize, 45413(0) pageins, 0(0) pageouts.
Networks: packets: 13087/6633K in, 9374/759K out.
Disks: 8123/523M read, 3442/66M written.PID COMMAND %CPU TIME #TH #WQ #POR #MRE RPRVT RSHRD RSIZE VPRV
329 ruby 99.3 00:48.80 4/1 0 36 46 1836K 264K 3504K 29M
こんな感じで、CPU Usage が 100% を超えることはなかった。
ただ、Ruby の Thread モデルは使い方次第だと思います。
以下のように、sleep して I/O 待ちにちょくちょくなるような
プログラムだと Thread の恩恵があると思います。
$ cat parallel.rb #!/usr/bin/env ruby count = 10 def func(id, count) i = 0; while (i < count) #puts "Thread #{i} Time: #{Time.now}" sleep(0.1) i = i + 1 end end puts "Started at #{Time.now}" thread1 = Thread.new{func(1, count)} thread2 = Thread.new{func(2, count)} thread3 = Thread.new{func(3, count)} thread4 = Thread.new{func(4, count)} thread1.join thread2.join thread3.join thread4.join puts "Ending at #{Time.now}"
実行すると、以下の結果
$ time ruby ./parallel.rb Started at 2013-05-13 23:40:09 +0900 Ending at 2013-05-13 23:40:10 +0900 real 0m1.224s user 0m0.106s sys 0m0.093s $
sleep する度に、thread が切り替わって並列で走行しているので
real で 1.2 sec という実行結果。
$ cat serial.rb #!/usr/bin/env ruby count = 10 def func(id, count) i = 0; while (i < count) #puts "Thread #{i} Time: #{Time.now}" sleep(0.1) i = i + 1 end end puts "Started at #{Time.now}" func(1, count) func(2, count) func(3, count) func(4, count) puts "Ending at #{Time.now}" $
$ time ruby ./serial.rb Started at 2013-05-13 23:41:01 +0900 Ending at 2013-05-13 23:41:05 +0900 real 0m4.241s user 0m0.105s sys 0m0.093s $
こちらは、馬鹿正直に4回繰り返しているので
4.2 sec ほど必要です。
まぁ、私は Ruby レベル低すぎて Thread 使える人間ではございません。