Hatena::ブログ(Diary)

ohacの日記

2010-05-27 twitter記法のテスト このエントリーを含むブックマーク

2009-12-11

dRubyで並列処理

(※12月の1日から25日まで、日替わりで Ruby の Tips を紹介するイベント、 Ruby Advent Calendar jp: 2009 の 11 日目です。昨日は no6v さんでした。明日は id:willnet さんの予定です。)


RubyのThreadは時分割なので並列処理を行いたいときにちょっと困ります。そんなときにはdRubyを使ってみるのはいかがでしょうか。


http://gist.github.com/250118

#!/usr/bin/ruby
require 'drb/drb'
class MonteCarlo
  def initialize(seed)
    srand(seed)
  end
  def dice(n)
    best = rand
    n.times do
      r = rand
      best = r if r < best
    end
    best
  end
end

PROCESSORS = (ARGV[0] || 1).to_i

pids = []
workers = PROCESSORS.times.map do |i|
  uri = "druby://localhost:#{12345 + i}"
  pids << fork { DRb.start_service(uri, MonteCarlo.new(i)); sleep }
  DRbObject.new_with_uri(uri)
end

begin
  n = 5000000 / PROCESSORS
  q = Queue.new
  sleep 0.1
  ts = workers.map do |foo|
    Thread.start(foo) {|f| q.push(f.dice(n))}
  end
  ts.each{|t| t.join}
  p q.size.times.map{q.pop}.min
ensure
  pids.each {|pid| Process.kill(:TERM, pid)}
end

このサンプルコードは500万回の疑似乱数の中から最も0に近い実数を見つけだすというプログラムです。sleep 0.1の部分は見なかったことにしてください。サンプルなので内容はあまり意味がありませんが、要は並列処理したい部分をMonteCarloのdiceの中に書いてあげればいいわけです。このプログラムは以下のように並列処理数を指定して実行します。

$ ruby montecarlo.rb 2

この例では2つのプロセスを並列で動かします。最近のデュアルコアCPUでは2を指定しましょう。コアがもっと多い人は「界王拳4倍!」とか唱えながら4とかを指定するといいでしょう。コアが1つしかない方はヤムチャの気分になってあきらめてください。というのは冗談で、コア数を超えていても一応動きますので試してみてください。


メインのプロセスはforkで生成された子プロセス2つに命令を送り、待つためのスレッドがそれぞれ生成され、joinで終了を待ちます。


結果はQueueに格納され、これを個数分popして取り出し、その中から最小値を取り出して表示して終了します。Queueは便利ですね。


以下はベンチマークです。平均とかは取っていないのでかなり適当ですが。それにしてもやっぱりCore i7は速いですね。こんなことにしかこのPCを活かせていないのが悲しいですが。


Ruby1.8.7

プロセスCoreDuoCore i7
17.284s4.482s
23.882s 2.120s
33.898s 1.417s
43.876s 1.102s
53.695s 1.163s
63.754s 0.997s
73.716s 0.958s
83.666s 0.974s
93.738s 1.029s
103.713s 0.983s
113.905s 0.981s
123.776s 0.952s
133.697s 0.934s
183.816s 0.896s
323.669s 0.907s
644.036s 0.942s
964.643s 0.973s

Ruby1.9.1

プロセスCoreDuoCore i7
14.675s 2.082s
22.677s 1.112s
32.656s 0.791s
42.609s 0.625s
52.746s 0.705s
62.649s 0.656s
72.581s 0.641s
82.626s 0.616s
92.691s 0.646s
102.617s 0.632s
112.619s 0.629s
122.573s 0.633s
162.705s 0.639s
322.619s 0.662s
643.144s 0.625s
963.010s 0.797s

なお、このプログラムlinuxでしか試していませんので、動かなかった場合はがんばって動くようにしてもらえると助かります。また、MacRubyな方はこんなことしなくても普通にスレッドで動かせばよいらしいです。(くやしー!)

2009-03-26

new Objectはスケールするか?

$ cat a.scala 
import scala.concurrent.ops._
import java.lang.System.{currentTimeMillis => curt}

def foo(n: Int) = {
  (0 until n).foreach { _ => new Object }
}

def bench(weight: Int, ncore: Int) {
  val s = curt
  (0 until ncore).map(_ => future(foo(weight))).toList.foreach(_())
  val e = curt
  println("%d: %d ms".format(ncore, e - s))
}

(0 to 3).foreach { _ =>
  bench(40000000, 1)
  bench(20000000, 2)
  bench(10000000, 4)
}
$ scala a.scala 
1: 868 ms
2: 598 ms
4: 594 ms
1: 783 ms
2: 587 ms
4: 596 ms
1: 784 ms
2: 587 ms
4: 593 ms
1: 785 ms
2: 590 ms
4: 589 ms

CoreDuoで実行したので4はあまり意味がないです。

2009-03-12

2008-12-18

コミットログの出だしの単語ベスト30

取ってみた。

              present -s  past ing
----------------------------------
fix           771     8   70
add           729         102    4
merge         746         13
make          304
use           269
remove        188         29
update        120         20
don't         121
move          115         5
allow         91
change        72          10
test          66      7
simplify      68
avoid         58
handle        57
improve       41          9
minor         47
do            46
show          32
refactor      32
import        12          20
ignore        30
clean         19          10
replace       23          5
only          27
more          27
pull          25
get           25
convert       25
correct       9           15

少し前のmercurial-crewのログから最初の2単語を抽出して、sort,uniq -c,cutとかいろいろ使いながら、半分手作業でまとめました。

ベスト160まで取りましたが、長くなるのでとりあえずここまで。

できればこの後どういう単語につながるパターンが多いのか調べていきたい。