それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

ruby のマルチスレッドでCPUのマルチコアを使い切れない・・・

ruby マルチスレッド を many core の CPU で動かしても 100% で止まる。

とまるんですよ。i7 のCPUリソース余って勿体ない。

GIL ( global interpreter lock )

rubyYARVで Global Interpreter Lock の中で動作するので、マルチスレッドでもメニーコアを使い切れない。

CPU 使用率が100%で頭打ち

4コアあって400%まで出せるとしても、100%でとまる。

マルチスレッドといっても、RubyMRI)で同時に実行されるスレッドは1本なんですよ。

スレッドが同時に動いてるということはない。1つが動くとほかは止まってる。

主に、IOブロックで実行コンテキストを切り替える。なので、

使い切るには?

  • jruby を使う
  • rubinius を使う
  • 小さいコマンドに分割する。

jruby を使えば、GILの影響を受けずに、マルチスレッドでマルチコアを使い切れることを確認。

マルチスレッドでも、ロックなどをしっかりしないとダメ

ここをさらっと読んだけど、Atomicにちゃんとスレッドセーフしないと

http://www.jstorimer.com/blogs/workingwithcode/8085491-nobody-understands-the-gil

サンプル

array = []

5.times.map do
  Thread.new do
    1000.times do
      array << nil
    end
  end
end.each(&:join)

puts array.size
takuya@~/Desktop$ ruby test.rb
5000
takuya@~/Desktop$ jruby test.rb
4727

スレッドで同時実行されて壊れる。

mutex を使って、スレッドセーフにしたら。。。

マルチスレッドで変数が壊れるので、ちゃんとSynchronizedにしてみたら・・・

require 'thread'
require 'monitor'
array = []

m = Mutex.new
5.times.map do
  Thread.new do
    1000.times do
      m.synchronize do
        array << nil
      end
    end
  end
end.each(&:join)

puts array.size
takuya@~/Desktop$ for i in {1..100}; do jruby test.rb  ;done;
5000
5000
5000
5000
5000
5000
5000
5000
5000

壊れない。

むしろ、sync しないで動くMRI の方が気持ち悪い気もしてきた。

とにかく上限100%

MRIはスレッド使い切れないことが分かった。

しっかりスレッドセーフなプログラムを書いて試さないと、jRubyですらどう動くか予想がつかない。・・・怖い。

解決策は小さいプロセスを大量にexec することかも

xargs てきに小さいプロセス起動しまくるってのが良いのかも。

参考資料

Nobody understands the GIL

Nobody understands the GIL - Part 2: Implementation

Rubyのスレッド周りの話 - Qiita