昨日の続き。Ruby でスレッドを使った ab (Apache Bench) もどきを作ってみる。
Ruby は初めて使うので、おそらく作法的に変なことをしているであろう サンプルスクリプト。 ソースは以下。(Ruby は Kondara-2.1 附属の、ruby 1.6.5 (2001-09-19) [i586-linux])
#!/usr/bin/ruby require 'net/http' require 'getoptlong' opts = {} opts_p = GetoptLong.new( [ "--c", "-c", GetoptLong::REQUIRED_ARGUMENT ], [ "--n", "-n", GetoptLong::REQUIRED_ARGUMENT ], [ "--u", "-u", GetoptLong::REQUIRED_ARGUMENT ] ) opts_p.each do |opt, arg| opts[opt] = arg end url = opts["--u"].to_s proto, host, path = url.split("/+", 3) path = "/" + path threads = [] total_requests = 0 success = 0 failed = 0 count = opts["--c"].to_i counts = (1 .. count).to_a limit_requests = opts["--n"].to_i start_time = Time.now for i in counts threads << Thread.new(host) { |host| while total_requests < limit_requests h = Net::HTTP.new(host, 80) resp, data = h.get(path, nil) if resp.code == "200" success = success + 1 else failed = failed + 1 end total_requests = total_requests + 1 end } end threads.each { |aThread| aThread.join } end_time = Time.now t = end_time - start_time requests_per_second = sprintf('%.2f', total_requests / t.to_f) puts "Concurrency Level: #{count}" puts "Time taken for tests: #{t.to_f} seconds" puts "Complete requests: #{success}" puts "Falied requests: #{failed}" puts "Requests per second: #{requests_per_second} [#/sec] (mean)"
特に GetoptLong の使い方は絶対間違っている気がするが・・・
起動方法などは昨日の fake_ab.pl と同じ。
$ ./fake_ab.rb -c 10 -n 1000 -u http://aqua/
とする。
...
fake_ab.pl と、fake_ab.rb、本物の ab で実行してみた結果 (-c 10 -n 1000) の Requests per second を以下に。
/index.html.dist (静的ファイル) Perl 236.42 Ruby 124.18 ab 343.05 /index.shtml (SSI html) Perl 178.88 Ruby 106.28 ab 229.52 /cgi-bin/printenv.cgi (環境変数を出力するだけの mod_perl CGI) Perl 110.07 Ruby 88.74 ab 118.72 /cgi-bin/index.cgi?rm=view (PostgreSQL に接続して数十レコードを閲覧する mod_perl CGI) Perl 12.56 Ruby 12.28 ab 12.53
(サーバ側の負荷が)軽い場合は ab > Perl > Ruby 。 重い場合は ab == Perl >= Ruby 。これだけ見ると、この処理 (標準モジュールでの HTTP GET) に関しては Ruby は Perl より重い、と結論付けたくなるのだが、gkrellm で、クライアント側の転送量を観察していると、 なぜか Ruby の時だけ output が多いことに気がつく。
ethereal でパケットキャプチャしてみたところ、Ruby は http://aqua/ を GET するにあたって、 DNS に AAAA レコードと A レコードをリクエストしている。Perl と ab は、A レコードしか引いていない。 もしかして、Ruby が遅いのはこれが原因?
ということで、名前を引かなくていいように URL を IP アドレスで指定した結果。
/index.html.dist (静的ファイル) Perl 244.24 Ruby 191.55 ab 328.84 /index.shtml (SSI html) Perl 197.33 Ruby 191.64 ab 222.57 /cgi-bin/printenv.cgi (環境変数を出力するだけの mod_perl CGI) Perl 113.77 Ruby 107.18 ab 122.09 /cgi-bin/index.cgi?rm=view (PostgreSQL に接続して数十レコードを閲覧する mod_perl CGI) Perl 12.50 Ruby 12.42 ab 12.19
Perl と Ruby の差が接近。つまり、Ruby の IPV6対応な名前解決が足を引っ張っていたらしい。 ベンチマークって、こういうことがあるから簡単に結論づけてはいけないわけですな。 奥が深い。