CGLIB と ASM
昨日の「徳澤直子ちゃんを語る会」で,「Aspect を適用するとメソッド呼び出しが 20 倍くらい遅くなる」という話をひがさんから聞きました.まぁ,それくらいは遅くなるだろうなぁと思ったりはするわけですが,っていうかもっと遅くなっても不思議はない気もするわけで.
そんなわけで (どんなわけで?),ちょっと確認してみました.
ものすごく単純なインタフェース
public static interface Hoge { void foo(); void bar(); }
と,その実装クラス
public static class HogeImpl implements Hoge { public void foo() { } public void bar() { } }
そして何もしないインターセプタ
public class VoidInterceptor extends AbstractInterceptor { public Object invoke(MethodInvocation arg0) throws Throwable { return null; } }
を用意.
HogeImpl
の foo にだけアスペクトをかけて 1 億回ばかり実行してみました.
その結果...
アスペクトなし | アスペクトあり (CGLIB) | |
---|---|---|
foo() | 2,156ms | 31,917ms |
bar() | 2,406ms | 4,641ms |
ふむ.アスペクトなしの場合に foo()
と bar()
で微妙に違うのが謎ですが,アスペクトを適用したクラスの場合,実際にそのアスペクトが適用されないメソッドでも 2 倍くらい,本当にアスペクトが適用されたメソッドの場合は 15 倍くらい遅くなってますね.
そんなに遅いという気はしないわけですが,以前某巨大掲示板に ASM を使った方が CGLIB より速いという話があった気のせいが,あれ? ぐぐっても見つからないよ?
ともあれ (JW),そんなわけで (どんなわけで?),S2 の AopProxy
を ASM を使うように直してみました (これがやりたかっただけだったりして).その結果...
アスペクトなし | アスペクトあり (CGLIB) | アスペクトあり (ASM) | |
---|---|---|---|
foo() | 2,156ms | 31,917ms | 20,323ms |
bar() | 2,406ms | 4,641ms | 2,422ms |
ふむ.foo()
の呼び出しは CGLIB よりも 3 割ばかり速くなりましたが,劇的というほどの違いはない感じ.bar()
の呼び出しが速いのは多少イカサマで,生成したクラスでオーバーライドしていないから.(^^;
実は最初,ASM を使っても全然速くならなかったんですよね.結局,MethodInvocationImpl
のインスタンス生成が遅いことが分かって,どうやらそれはインスタンスフィールドの数に左右されるらしいんですね.そこでそのあたりをチューニング (?) してどうにか上記の数字まで改善できたという次第.ということは,その辺りを見直せば CGLIB でももっと速くできるということかも.
とりあえず,ASM の API だけは簡単でした♪ 低水準なんで面倒この上ないというか,Java でアセンブラのソースを生成しているような雰囲気ですね.