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