オンメモリキャッシュサーバとして 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 と理解しました