君たちの愛したmruby-profilerは復活した(その2)

昨日の続きで、irepメンバーとIREP構造体中のirepメンバーのインデックスがなぜ必要だったのかを説明します。

プロファイル情報はどこにあるの?

  プロファイラはかかった時間とか実行回数を(メソッド、行、命令)レベル(Profilerによる)で数えて後から分かりやすく表示するソフトです。ですので、どこかに数えた結果を持っていなければなりません。

mruby-profilerの場合はVMの命令ごとに実行時間を数えます。VMの命令はIREP構造体に格納されているので、命令毎の実行時間もIREP構造体に入れておくのが一番いいのですが、mruby本体に手を入れる必要があり、メモリ制約の厳しいmrubyにプロファイル用のメンバーを入れて貰うのは期待できません。

そこで、mruby-profilerはprof_irep構造体というプロファイルのための構造体をIREP構造体ごとに用意して、そこに実行時間などを格納するようにしました。この方法だと、問題が1つ残ります。IREP構造体とprof_irep構造体をどうやって結びつけるかです。

命令の実行時間を計測するために、code_fetch_hookの仕組みを使うのですが、code_fetch_hookは現在実行中のIREP構造体は引数としてもらえるのですが、それに対応するprof_irep構造体はなんとかIREP構造体から得る必要があります。

やりたいことは単なる連想配列なんですが、実行時に実行対象のIREP構造体はメソッドの呼び出しとかリターンとかで頻繁に変わりますし、VMの命令実行ごとに行われる処理なので、とにかく高速におこなうことが必要です。ハッシュテーブルを作るとかだとおそらくすごく遅くなるでしょう。

これまでの方法

 これはでは、mrb->irepがすべてのIREP構造体が入っている配列であることを利用して、mrb->irep配列と同じ長さのprof_irep配列を用意して、IREP構造体に対応するprof_irep構造体を格納します。こうすれば、prof_irep配列を経由して、IREP構造体からprof_irep構造体が結び付けられるわけです。

こんな感じのコードになります。

  idx = irep->idx                           /* IREP構造体のインデックス値を得る */
  current_prof_irep = prof_irep_array[idx]; /* IREP構造体に対応するprof_irep構造体を得る */

IREP構造体をGC出来ないことを除けば、かなり効率のよい方法です。irep->idxとかmrb->irepが無くなった今、別の方法を考えなければなりません。半年間いい方法が浮かばなかったのですが、ふとこの方法ほどではないのですが効率的な方法が浮かんだのでそれを実装しました。それが、今回の復活の決め手です。

それでは次回、ごきげんよーさようなら