Rails 実行中に GC にかかっている時間

前回の記事では Rails 実行中のメソッド呼び出しすべてをトレースしてみましたが、今回のターゲットは Garbage Collection に要している CPU 時間です。前と同じく、render :text => "Hello World!" なアプリに対して Apache Bench から 1万リクエストの負荷を与え、その間の GC 時間をトレースしました。


DTrace の結果をみると、計 75 回の GC が確認され、合計で約 5 秒程度 の CPU 時間を消費しているようです。ab の実行されていた約 40 秒間は、(アクティビティモニタで見ていると) ruby プロセスの CPU 使用率がほぼ 100% 近くに張り付いていたので、そのうちの約 1/8 が GC に使われていたということでしょうか。


# メソッド呼び出しのときと違い、トレース対象をひっかける回数も多くはないので、 DTrace をしかけたことによるオーバーヘッドの影響はほとんどありませんでした。(DTrace をしかけたときとそうでないときで、ab の結果はほとんど同じでした)


以下、作業ログです。


環境

  • 処理系、ライブラリのバージョンは以下のとおり
% /usr/bin/ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]

% dtrace -V
dtrace: Sun D 1.2.2

% rails -v
Rails 2.2.2

# 単純に render :text => "Hello World!" するだけのアプリを用意
% curl  http://127.0.0.1:3000/posts
Hello World!%

RubyGC 時間をトレースする DTrace スクリプト

ruby$target:::gc-begin
{
  self->start = vtimestamp;
  }

ruby$target:::gc-end
/self->start/
{
  this->elapsed = vtimestamp - self->start;

  @count["COUNT"] = count();
  @sum["SUM[us]"] = sum(this->elapsed);
  @avg["AVG[us]"] = avg(this->elapsed);
  self->start = 0;
}

dtrace:::END
{
  normalize(@sum, 1000);
  normalize(@avg, 1000);

  printf("\n");
  printa("%-15s %10@d\n", @count);
  printa("%-15s %10@d\n", @sum);
  printa("%-15s %10@d\n", @avg);
}

DTrace をしかけずに ab だけ実行

  • まぁこんなものでしょう
% ab -c 10 -n 10000 http://127.0.0.1:3000/posts
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)

(snip)

Server Software:        Mongrel
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /post
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   38.598 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      4730018 bytes
HTML transferred:       120000 bytes
Requests per second:    259.08 [#/sec] (mean)
Time per request:       38.598 [ms] (mean)
Time per request:       3.860 [ms] (mean, across all concurrent requests)
Transfer rate:          119.67 [Kbytes/sec] received

今度は、Dtrace をしかけておいて...

% sudo dtrace -s rb_gc.d -p 40211
dtrace: script 'rb_gc.d' matched 3 probes

再び ab を実行

  • DTrace をしかけてないときとほとんど同じ結果です
  • GC のはじまりと終わりをひっかけているだけなので、ほとんどオーバーヘッドはかかっていないようですね
% ab -c 10 -n 10000 http://127.0.0.1:3000/posts
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)

(snip)

Server Software:        Mongrel
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /post
Document Length:        12 bytes

Concurrency Level:      10
Time taken for tests:   39.412 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      4730018 bytes
HTML transferred:       120000 bytes
Requests per second:    253.73 [#/sec] (mean)
Time per request:       39.412 [ms] (mean)
Time per request:       3.941 [ms] (mean, across all concurrent requests)
Transfer rate:          117.20 [Kbytes/sec] received

DTrace の結果

  • 5373989 マイクロ秒、すなわち 約 5 秒程度、GC の実行に CPU 時間が消費されています
% sudo dtrace -s rb_gc.d -p 40211
dtrace: script 'rb_gc.d' matched 3 probes
^C
CPU     ID                    FUNCTION:NAME
0      2                             :END
COUNT                   75
SUM[us]            5373989
AVG[us]              71653