Seasarをチューニング?

ActionをSeasarで生成するようにしてWebWorkを使っているのだが、これが、ものすごく遅い。
どうも、aspectが指定された、instance="prototype" となっているコンポーネントの生成が遅いようなのだ。

こんな感じのテストプログラムを作ってみた(SampleBeanは省略)。

public class Main {
    public static void main(String[] args) throws Exception {
        S2Container container = S2ContainerFactory.create("test.dicon");
        final int count = 1000;
        
        // Warmup
        container.getComponent("test_simple");
        container.getComponent("test_aop");
        
        {
            long start = System.currentTimeMillis();
            for (int i = count; i > 0; i--) {
                SampleBean sb = (SampleBean)container.getComponent("test_simple");
            }
            long end = System.currentTimeMillis();
            System.out.println("Elapsed = " + (end - start));
        }
        {
            long start = System.currentTimeMillis();
            for (int i = count; i > 0; i--) {
                SampleBean sb = (SampleBean)container.getComponent("test_aop");
            }
            long end = System.currentTimeMillis();
            System.out.println("Elapsed = " + (end - start));
        }
    }
}

diconファイルはこんな感じ。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="test">
	<include path="aop.dicon"/>
	<component name="test_simple" instance="prototype" autoBinding="none"
		class="test.SampleBean"/>
	<component name="test_aop" instance="prototype" autoBinding="none"
		class="test.SampleBean">
		<aspect pointcut="execute">aop.traceInterceptor</aspect>
	</component>
</components>

実行結果は、

Elapsed = 15
Elapsed = 9281

で、かなり遅い。
プロファイラで調べてみると、net.sf.cglib.proxy.Enhancerまわりがかなりの時間を占めているようだ。しかし、毎回エンハンス処理って必要なのか?生成されるオブジェクトのクラスも毎回異なるクラスになっているようだが、その必要はあるのだろうか。さらに調べてみた。
net.sf.cglib.core.AbstractClassGenerator#create(Object)の以下の部分を見つけた。

        if (cache2 == null) {
            cache2 = new HashMap();
            cache2.put(NAME_KEY, new HashSet());
            source.cache.put(loader, cache2);
        } else if (useCache) {
            Reference ref = (Reference)cache2.get(key);
            instance = ( ref == null ) ? null : ref.get(); 
        }
        if (instance == null) {
            Object save = CURRENT.get();

どうもキャッシュしようとしているのだが、うまくヒットしていないように思える。調べてみると、キャッシュのキー中のAopProxy.MyCallbackFilterが毎回生成されるためキーとして異なるものとして扱われているようだ。これを修正してみることにする。

とりあえず、AopProxyにComponentDefを渡して生成することを可能として、ComponentDefをMyCallbackFilterが参照するようにし、同じComponentDefを参照しているMyCallbackFilterはequals()でtrueを返却するように変更した(コードは省略)。

今度の実行結果は、

Elapsed = 16
Elapsed = 203

となり、生成されるコンポーネントのクラスも毎回同じになった。速度的にはもう一声ほしいところだが、とりあえずは、このままにしておく。