A Day in the Life

Ruby で並列実行処理を簡単に書く

Ruby で並列実行処理を簡単に書く

Ruby は 1.8 だとグリーンスレッドだし、1.9 でも Giant VM lock のためネイティブスレッドの実行は一つのため、マルチコアを生かした処理をかんたんに書くのがいささか面倒だったりしますね。

で、 parallel というライブラリを使うと fork を使って抽象化してくれるのですごく簡単にかける。

ふつうに Parallel#each/map の中で並列処理させたいコードを書くだけ。戻り値が #map なら取得できる。 以下は zlib で gzip 圧縮させた結果。

require 'rubygems'
require 'parallel'
require 'pathname'
require 'benchmark'
require 'zlib'

compress_files = lambda {|file|
  Zlib::GzipWriter.open(file.realpath.to_s + '.gz') {|gz| gz.puts file.read }
}

puts "#{Parallel.processor_count} procesor(s)"

files = Pathname.glob('*.log')

Benchmark.bm do |x|
  x.report('normal') { 
    files.each &compress_files
  }
  x.report('thread') { 
    Parallel.each(files, :in_threads => Parallel.processor_count, &compress_files)
  }
  x.report('fork') { 
    Parallel.each(files, &compress_files)
  }
end

CPU が i7 の環境(2コア・仮想4スレッド)では

4 procesor(s)
      user     system      total        real
normal 16.740000   2.100000  18.840000 ( 19.154235)
thread 19.550000   2.350000  22.050000 ( 24.297111)
fork  0.010000   0.040000  27.890000 ( 12.871694)

と、19秒強 -> 13秒弱と簡単に速度を上げることができた。みんな大好き parallel_test も内部では parallel を利用している。べんり。

あと Thread での並列処理も抽象化も簡単に書けるので、select で IO 待ちするような処理も直感的に書いてスピードアップできる。

require 'rubygems'
require 'parallel'
require 'open-uri'

urls = (1..10).map {|i| "http://example.com/?query=#{i}" }
results = Parallel.map(urls, :in_threads => 10) {|url|
  puts "download: #{url}"
  body = open(url).read
  puts "downloaded: #{url}"
  body
}

Thread で 10並列でダウンロードで全部結果が帰ってきたら次、みたいな。

download: http://example.com/?query=1
download: http://example.com/?query=2
download: http://example.com/?query=3
download: http://example.com/?query=4
download: http://example.com/?query=5
download: http://example.com/?query=6
download: http://example.com/?query=7
download: http://example.com/?query=8
download: http://example.com/?query=9
download: http://example.com/?query=10
downloaded: http://example.com/?query=2
downloaded: http://example.com/?query=5
downloaded: http://example.com/?query=4
downloaded: http://example.com/?query=1
downloaded: http://example.com/?query=9
downloaded: http://example.com/?query=7
downloaded: http://example.com/?query=3
downloaded: http://example.com/?query=8
downloaded: http://example.com/?query=10
downloaded: http://example.com/?query=6
記事の一覧 >

関連するかもエントリー

Ruby の http ライブラリの通信を表示する http-dump を作った
f:id:secondlife:20140110170809p:plainRuby 上で http を叩いた通信見たい時に、毎回同じ事をやってるので抽象化して http-dump というライブラリを作った。https://github.com/hotchpotch/http-du...
f:id:secondlife:20140110170809p:plainRuby 上で http を叩いた通信見たい時に、毎回同じ事をやっ...
アドベントカレンダーを電子書籍で読めるサービスを作った
http://advent-calendar2readlists.herokuapp.com/f:id:secondlife:20140111143236j:plainQiita や Adventar、ATND のアドベントカレンダーがオフラインの電子書籍で読みたい、と思ったので...
http://advent-calendar2readlists.herokuapp.com/f:id:secondlife:2014011...
ruby の Net::HTTP と curb (curl の ruby binding) の単純な速度比較
ruby の Net::HTTP と curb (curl の ruby binding) の単純な速度比較マラちゃん(懐かしいなこの呼び方)がLLごとのでとっていたので、Ruby だとどうなんだろうと思って curb と Net::HTTP とで測定。$ ruby -I/usr...
ruby の Net::HTTP と curb (curl の ruby binding) の単純な速度比較マラちゃん(懐かしいなこの呼び方...