K.Sasada's Home Page

こめんとのついか

こめんとこめんと!

message

please add long comment :).

_4(Tue)

Ruby VM アドベントカレンダーの 4 日目です.

これまで,Ruby 2.0 から導入される caller まわりの話をご紹介してきました.今日はバックトレースまわりの最後の話です.多分.

Ruby では,例外発生時にバックトレース情報が保存されます.保存しておかないと,あとで(例えばスタックを巻き戻した後で)その情報を参照できないためです.

Ruby 1.9 以前では,例外発生時に caller が返すようなバックトレース情報(文字列の配列)を生成し,例外オブジェクトが保持しているようになっていました.しかし,これはちょっと遅いです.フレームサイズ分,文字列オブジェクトを生成して,配列に格納しなければなりませんから.

そこで,Ruby 2.0 では,例外発生時には Thread::Backtrace というオブジェクトを生成することにしました.これは,Exception オブジェクトからは直接は参照できない特殊なオブジェクトになっています(ObjectSpace.each_objects で頑張れば参照できます).

Exception#backtrace が呼ばれたとき,Thread::Backtrace オブジェクトを文字列配列にする処理が走り,従来の(caller が返すような)文字列配列を初めて作成します.このようにすることで,Exception#backtrace を参照しない場合(制御フローとして例外を利用しているような場合)のオーバヘッドを削減しています.

では,実際に測ってみましょう.

require 'benchmark'

def rec n=100, &b
  n == 0 ? yield : rec(n-1, &b)
end

max = 100_000

Benchmark.bm{|x|
  x.report(" 10"){
    max.times{
      begin
        rec(10){raise}
      rescue # ignore raise
      end
    }
  }
  x.report("100"){
    max.times{
      begin
        rec(100){raise}
      rescue # ignore raise
      end
    }
  }
}
__END__
ruby 1.9.3p332 (2012-11-15 revision 37660) [i386-mswin32_100]
             user     system      total        real
 10 rec  2.262000   0.031000   2.293000 (  2.352799)
100 rec 10.437000   0.141000  10.578000 ( 10.638851)

ruby 2.0.0dev (2012-12-01 trunk 38127) [i386-mswin32_100]
            user     system      total        real
 10 rec 0.640000   0.015000   0.655000 (  0.652583)
100 rec 2.199000   0.031000   2.230000 (  2.280790)

全スタックフレームの,バックトーレス情報を生成するために必要になる情報 (*1) を保存をするので,どうしてもスタックトレースのサイズに比例した時間がかかることになってしまいますが,しかし 1.9 に比べて随分と速くなっていることがわかるのではないでしょうか.

(*1) 具体的には iseq + pc になります.本当はもうちょっと複雑ですが.

(例外的にしか発生されない例外の生成が速くなって,嬉しい人は限定的だとは思いますが...)

なお,caller および caller_locations は,この Thread::Backtrace オブジェクトを1度生成してから,それをベースに文字列の配列,もしくは Thread::Backtrace::Location の配列を生成します.

というように,Ruby 2.0 からバックトレースまわりの構造ががらっと変わりました.詳しく知りたい人は vm_backtrace.c を読んでみて下さい.

では,この辺で.


三日坊主が回避されたので,そろそろ力尽きるかもしれない.


好きなだけ長いコメントをどうぞ。

お名前


back

tton 記述が使えます。YukiWikiな記述してりゃ問題ありません。

「行頭に#code」 と、「行頭に#end」 で挟むと、その間の行は pre で囲まれます。プログラムのソースを書くときに使ってください。

例:

#code

(なんかプログラム書く)

#end

リンクは

[[なまえ|http://www.example.org]]

とか

[[http://www.example.org]]

で貼れます。

$Date: 2003/04/28 10:27:51 $