インラインメソッドキャッシュ
先ほど、Ruby のメソッド呼び出しの処理内容を示しましたが、その中でメソッド定義の検索、というものがありました。
- レシーバのクラス (これを klass とする。klass = recv.class) からメソッドの定義を検索する。
o もし klass で method メソッドが定義されていなければ、klass の親クラスで定義されていないかを検索する。これを method メソッドの定義が見つかるまで繰り返す。
これです。
この検索は、見てのとおり負荷が大きい (最悪、ハッシュの計算を何度もやることになります) ため、現在の Ruby 処理系ではグローバルメソッドキャッシュという最適化をしています。これについての詳細は RHG を読んでいただくとして (第15章 メソッド)、要するに「以前にそのクラス klass で検索したメソッド method の定義は再定義されない限り変わらない」という事実を利用して、キャッシュ表に検索した結果を書き込んでおき、あとでその結果を利用する、というものです。
で、これをもっと高速化したいなぁ、と思って作ったのがインラインメソッドキャッシュです。
前回、定数アクセスを高速化するためにインラインキャッシュを利用しましたが、まったく同様のことをメソッド検索結果にも適用しています。
たとえば、
num.times{
recv.method()
}
という繰り返しを行うプログラムがあった場合、num 回実行される recv.method() で呼び出すメソッドの定義は毎回同じだろう、と推測されます。それならば、毎回ここでメソッド検索を行うのは無駄なので、その send 命令に検索結果をキャッシュしてあげればいいのではないか、と思うのは人間として当然ですね。そこで、これを行うのがインラインメソッドキャッシュ、というわけです。
このインラインメソッドキャッシュはたいていうまくいってます。
これについての詳細な情報 (実装方法とその評価) は私の情報処理大会、全国大会の予稿 (プログラム言語Ruby におけるメソッドキャッシング手法の検討 (PDF)) をご参照ください。
YARV ではグローバルメソッドキャッシュも併用しており、インラインメソッドキャッシュにひっかからなかった場合にはそちらを利用することになります。たとえば、Ruby C API 経由でメソッド呼び出しを行った場合は、グローバルメソッドキャッシュだけを利用することになります。
余談ですが、メソッド定義検索の高速化はオブジェクト指向言語の処理系で研究テーマのひとつとなっており、他にもさまざまな手法が提案されています。
YARV Maniacs 【第 6 回】 YARV 命令セット (3) メソッドディスパッチ