JRubyを触ってみた。

oktGIL,JRuby,Python,Ruby,スレッド,マルチスレッド,マルチプロセス

oktです。
RubyKaigi2009に触発されたわけではありませんが、今日はRubyネタです。

以前、Python,Rubyでマルチスレッド・マルチプロセスでの並列処理プログラムの実行速度の比較をしました。最近になって「JRubyにはGILがない」ということを知りましたので、JRubyを使った場合の比較をしてみようと思います。

以下のコード(sample.rb)は以前のものの使い回しです。

for num in 1..4
    Thread.start(0) do |s|
        n = s
        while n < 100000000
            n = n + 1
        end
    end
end
Thread.list.each do |s|
    if Thread.current != s
        s.join
    end
end

今回はJRuby 1.3.1(1.9互換モード) とRuby 1.9.1p0で比較してみます。
実行環境は以前と同様CentOS5(VMware Server上で動作)で、仮想CPUを2個に設定しています。

JRuby 1.3.1 で実行した場合
$ time jruby -v test.rb
jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d) (Java HotSpot(TM) Client VM 1.6.0_14) [i386-java]  
real 0m44.019s
user 0m22.617s
sys 0m25.657s

Ruby 1.9.1p0 で実行した場合

$ time ruby -v test.rb
ruby 1.9.1p0 (2009-01-30 revision 21907) [i686-linux]  
real 0m12.755s
user 0m12.699s
sys 0m0.032s

JRuby 1.3.1実行中のtopコマンド
top - 17:40:15 up 3:43, 2 users, load average: 0.29, 0.37, 0.29
Tasks: 69 total, 1 running, 68 sleeping, 0 stopped, 0 zombie
Cpu0 : 18.0%us, 34.0%sy, 0.0%ni, 48.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu1 : 33.7%us, 23.8%sy, 0.0%ni, 41.6%id, 1.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 385400k total, 207980k used, 177420k free, 60424k buffers
Swap: 1048568k total, 0k used, 1048568k free, 98128k cached
 
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3280 okt 22 0 655m 24m 9724 S 110 6.5 0:10.40 java -client -Djruby.memory.max=500m -Djruby.stack.max=1024k

2CPUのマシン上で4スレッド動作させてCPUがidle状態になっている割合もかなり高く、2CPU合計でCPU使用率が100%強はあまり効率よくなさそうですが、実行時間はなかなか健闘していますね。
ついでに4CPUのマシン上で動作させてみましたが、CPU使用率3倍くらいに上がって実行時間変わらず、というちょっと謎な状況でした。スレッドのスケジューリングが重たいのかな、と。適当ですが。

もう少し実践的なサンプルで比較しないとわかりませんが、ネットワークI/O中心でレスポンスの速さが求められたり、処理が単純で大量のコネクションを扱うプログラムだったりすると、JRubyでCPUをたくさん使うよりもC実装のRuby1.9のほうが向いていそうですね。

JRubyはちょっと残念な感じもしますが、ネイティブスレッドが実装されるという噂の2.0に期待してもうすこし勉強してみます。