もけへへ倶楽部 RSSフィード

2007-10-01

[] Rubyでスレッド

Rubyで http ごしにデータを取得するには、'open-uri' を使えば簡単に出来るけど、複数の URL から取得するとき、単純に

require 'open-uri'
e1 = open("http://www.hogehoge.com/").read
e2 = open("http://www.fugafuga.net/").read

と直列にしてしまうと最初のリクエストが完了するまでブロックされてしまい、読み終わってからようやく次のリクエストを出すことになってしまう。アイドル時間がもったいないのでなんとかしたい。そこでスレッドを使ったら無駄な待ち時間が減るんじゃなかろうかと思った。

なにかお題を…てことで、livedoorクリップはてなブックマークの両方から結果を取得するなんてどうだろう、と思って両APIを見てみた。

RESTしか使ったことねーから XML-RPC なんてわからねーよ!と思ったけど、Ruby での XML-RPC の使い方を動かしてみたらそのまま動いた。

Rubyでスレッドのやりかたは逆引きRuby - スレッドを見たらバッチリ、で完成。

require "xmlrpc/client"

def get_xmlrpc( host, path, method, *args )
	# Make an object to represent the XML-RPC server.
	server = XMLRPC::Client.new( host, path )

	# Call the remote server and get our result
	server.call( method, *args )	# <= *args で配列を展開して引数に渡す
end

def get_livedoor_clip_count( urls )
	get_xmlrpc( "rpc.clip.livedoor.com", "/count", "clip.getCount", *urls )
end

def get_hatena_bookmark_count( urls )
	get_xmlrpc( "b.hatena.ne.jp", "/xmlrpc", "bookmark.getCount", *urls )
end

# 普通に取得
def get_by_normal( urls )
	lc = get_livedoor_clip_count( urls )
	hb = get_hatena_bookmark_count( urls )
	return lc, hb
end

# スレッドを使って取得
def get_by_thread( urls )
	lc = hb = nil

	# LivedoorClip の件数取得スレッド
	tlc = Thread.new do
		lc = get_livedoor_clip_count( urls )
	end

	# HatenaBookmark の件数取得スレッド
	thb = Thread.new do
		hb = get_hatena_bookmark_count( urls )
	end

	# 全スレッドの終了待ち
	[tlc, thb].map {|t| t.join }

	return lc, hb
end

# ベンチマーク
require 'benchmark'

Urls = %w{
	http://clip.livedoor.com/
	http://b.hatena.ne.jp/
	http://del.icio.us/
}

lc = hb = nil

if ARGV[0] == "n"
	Benchmark.bm(8) do |x|
		x.report('normal:')	{ lc, hb = get_by_normal( Urls ) }
	end
else
	Benchmark.bm(8) do |x|
		x.report('thread:')	{ lc, hb = get_by_thread( Urls ) }
	end
end

Urls.each do |key|
	puts "\t#{key}\tlc=#{lc[key]}, hb=#{hb[key]}"
end

一度に両方やってしまうと、キャッシュされてしまうのか後に実行したほうが速くなってしまっていたので、コマンドラインの引数で処理を分けるようにした。

実行結果:

$ ruby test.rb n    # <= 通常版
              user     system      total        real
normal:   0.090000   0.070000   0.160000 (  0.350000)
	http://clip.livedoor.com/       lc=214, hb=365
	http://b.hatena.ne.jp/  lc=123, hb=1179
	http://del.icio.us/     lc=68, hb=838

$ ruby test.rb      # <= スレッド版
              user     system      total        real
thread:   0.060000   0.110000   0.170000 (  0.210000)
	http://clip.livedoor.com/       lc=214, hb=365
	http://b.hatena.ne.jp/  lc=123, hb=1179
	http://del.icio.us/     lc=68, hb=838

一応スレッドを使ったほうが実時間では速くなってるっぽい?スレッド切り替えとか入る分、システム時間は少し余分にかかってるっぽい。ユーザ時間まで減るのはわかりませんが…。

しかし、スレッドを使った場合でも使ってない場合と比べてそんなに複雑さは変わらないほど楽チンなので、これならやってみようっていう気になりますね。

トラックバック - http://d.hatena.ne.jp/mokehehe/20071001/thread