オンメモリキャッシュサーバとして druby を試してみた→失敗
ようわ memcached 的な用途に druby をつかえないかなーと思って試してみたけど現実を見せつけられた格好です, というログです.
実行結果
$ ruby drbclient.rb 100
user system total real
0.030000 0.010000 0.040000 ( 0.109558)
$ ruby drbclient.rb 1000
user system total real
1.020000 0.110000 1.130000 ( 3.246166)
$ ruby drbclient.rb 10000
user system total real
90.040000 4.640000 94.680000 (239.860364)
drbserver.rb – druby サーバ
require "drb/drb"
front = []
DRb.start_service("druby://:8888", front)
puts DRb.uri
sleep
drbclient.rb – druby クライアント
require "drb/drb"
require "benchmark"
DRb.start_service
there = DRbObject.new_with_uri "druby://:8888"
puts Benchmark::CAPTION
puts Benchmark::measure {
ARGV[0].to_i.times {|i| there << "hello world #{i}" }
}
試した感じ, オブジェクト数の増加とともに指数関数的なパフォーマンス劣化が生じる模様です.
追記 6/12(1:02)
druby 作者である id:m_seki さんご本人にご指摘をいただいたので見直したところ, サーバ側のフロントオブジェクトが Array で, かつ, << メソッドを使っているために遅いんだよ, と教えていただきました. 関さん, ありがとうございます.
理由として, 上記のコードの場合
- フロントオブジェクトを値渡ししている
- << メソッドの戻り値が self なので, << メソッドを呼び出すたびにサーバ→クライアントへフロントオブジェクトのコピーを渡してしまう
- 結果 O(N2) となる*1
という動きになっていたようです. 値渡しがこういう現象につながるとは想像がおよびませんでした.
というわけで, フロントオブジェクトを値渡し→参照渡しへ修正するとリニアな結果になりました. サーバ側は下記に記載している drbserver2.rb, クライアント側は上記の drbclient.rb です.
$ ruby drbclient.rb 100
user system total real
0.030000 0.000000 0.030000 ( 0.102600)
$ ruby drbclient.rb 1000
user system total real
0.340000 0.060000 0.400000 ( 1.136783)
$ ruby drbclient.rb 10000
user system total real
3.250000 0.570000 3.820000 ( 11.807745)
drbserver2.rb – druby サーバ参照渡し版
require "drb/drb"
front = []
front.extend DRbUndumped # ここ追加
DRb.start_service("druby://:8888", front)
puts DRb.uri
sleep
ほかのやりかたについては, この件に対する関さんの解説ページと参照渡しと値渡しに書いてあります.
*1:サーバ側で Marshal.dump(Array), クライアント側で Marshal.load(Array) で N2 と理解しました