Ruby 求教关于 Ruby 的多线程问题

mclxly · 2015年01月09日 · 最后由 spacewander 回复于 2015年01月19日 · 3964 次阅读

问题 1:为啥多线程比单线程慢这么多(大约 1 分钟)? 问题 2:为啥 Ruby 的 MD5 计算这么慢?同样的单线程版程序我用 php/go 写都只用大约 1 分钟。难道我使用不对?

多线程版:

#!/usr/bin/ruby
require 'digest/md5'

def fexe(value="")
    digest = Digest::MD5.hexdigest(value)

    for i in 1..10000
        digest = Digest::MD5.hexdigest(value)
    end
end

count = 0
arr = []

msg = "http://php.net/manual/en/function.printf.php";

for i in 1..10000    
   arr[i-1] = Thread.new {
      fexe(msg)
   } 
end

puts "size: #{arr.size}"

arr.each {|t| t.join; }
puts "done!"

输出:

size: 10000
done!

real    5m27.146s
user    5m19.881s
sys     0m2.870s

单线程版:

#!/usr/bin/ruby
require 'digest/md5'

def fexe(value="")
    digest = Digest::MD5.hexdigest(value)

    for i in 1..10000
        digest = Digest::MD5.hexdigest(value)
    end
end

count = 0
arr = []

msg = "http://php.net/manual/en/function.printf.php";

for i in 1..10000
    fexe(msg)
end

puts "size: #{arr.size}"
puts "done!"

输出:

size: 0
done!

real    4m16.798s
user    4m16.175s
sys     0m0.189s

为啥?因为它是 Ruby !!!

问题 1:GIL

$ time ruby md.rb size: 10000 done!

real 0m40.578s user 2m23.146s sys 0m2.253s

$ time ruby mds.rb size: 0 done!

real 1m24.515s user 1m27.306s sys 0m0.742s

用 jruby 就没这烦恼了

#2 楼 @kungs 有空我试试 Python 的多线程,看是不是也有 GIL 问题。但我觉得还有个可能是 Thread 对象太重。

#3 楼 @liprais JRuby 好比 PHP 的 HHVM,的确跑的快。

楼主可以解释下这段代码是要做什么吗? for i in 1..10000 arr[i-1] = Thread.new { fexe(msg) } end

ruby 是不支持真正的多线程的。@mclxly MRuby 是 thread not safe, 如果确实想用,最好用 JRuby。

#6 楼 @rocLv 应该倒过来说吧,MRuby 是本身是 thread safe, 个人写的代码不管是 MRuby 还是 JRuby 都需要自己注意 thread safe

#5 楼 @zeeler 我猜是把线程对象放进数组了

c ruby 是单线程的。它所实现的多线程相当于纤程,随便起多少个线程,最终只能占用 cpu 一个核心。 需要使用多个 cpu 的多线程,请换成 jruby. 或者自己用 c/或者 c++ 封装一个 gem 来多线程处理 md5. 然后 c ruby 调用。

呃,主要是 Digest::MD5.hexdigest 的实现多生成了一个重量级对象啦,你先 Digest::MD5.new 就好了。下面这个单线程的代码大约 1 分半钟

#!/usr/bin/ruby
require 'digest/md5'

M = Digest::MD5.new
def fexe value=""
    digest = M.hexdigest(value)
    10000.times {
      digest = M.hexdigest(value)
    }
end

count = 0
arr = []
msg = "http://php.net/manual/en/function.printf.php";

10000.times {
  fexe msg
}

puts "size: #{arr.size}"
puts "done!"

#9 楼 @zyfire Ruby 本身就是封装的 C 写的 md5, 和其他语言实现一样

是封装的 C 写的 md5, 但没有封装成多线程同步计算的。

@serco Maz 在 <代码的未来>中专门讨论了 thread safe, MRuby 默认不是 thread safe 的

#14 楼 @rocLv 没有看过 <代码的未来>,能详细说一下嘛? 目前我的理解是,MRI 下,所有 C 实现的 Ruby 核心库部分是线程安全的,因为有 GVL 在 Ruby 实现的部分标准库,用 Ruby 写的其他项目,不一定是线程安全的

#11 楼 @luikore 发现多线程版本开的线程越少,性能越接近于单线程。也许是因为这是个 CPU bound 的程序。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号