昨日の続き。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対応な名前解決が足を引っ張っていたらしい。 ベンチマークって、こういうことがあるから簡単に結論づけてはいけないわけですな。 奥が深い。
