パフォーマンス徹底比較
Seasar2 vs Spring
2006/04/12
株式会社電通国際情報サービス
ひがやすを
株式会社アークシステム
本間 宏崇
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
1
目的
• DIコンテナの実装によるパフォーマンスの
違いを明らかにする
• DIコンテナが行う処理の中で、どこに時間が
掛かるのかを明らかにする
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
2
ベンチマーク測定環境
• ハードウェア
– HP ProLiant DL360 G4p
– CPU: Intel Xeon 3.80GHz (2 CPU)
– Memory: 4GB
• ソフトウェア
– OS: Red Hat Enterprise Linux AS 4 Update 3
(x86)
– Java: 1.5.0_06 (Sun)
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
3
測定アプリケーション
• DIコンテナ
– Seasar 2.4 beta1 (2006/03/27)
– Spring 2.0 M3 (2006/03/08)
• ベンチマークプログラム
– 自作
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
4
測定方法
• VMオプション
•
•
•
•
•
-Xmx1024M
-Xms1024M
-XX:PermSize=384M
-XX:MaxPermSize=384M
fork=true
• JVMのキャッシュをクリアするため
• 5回実行し、最大・最小を除いた3回の平均値
を採る
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
5
DIコンテナがやっていること
• コンテナ生成
– XML読み込み(DOMやSAX)
• 定義からコンポーネントを組み立てる
– DI
• リフレクション
– リフレクション情報を取得しキャッシュする
– リフレクションを使用しプロパティへアクセスする
– AOP
• バイトコード作成
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
6
• それぞれの処理について、パフォーマンスを
見ていきましょう
• まずは、XML読み込みを行っている、コンテナ
生成処理からです。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
7
コンテナ生成
• コンテナ生成時の内部処理
– Seasar
• SAX
• リフレクション情報をキャッシュ
– Spring
• DOM
• ※リフレクション処理はここでは行わない
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
8
• コンテナへ入力する設定ファイル
– Seasar
<components>
<component name="nullBean00000" class="xxx.NullBean00000" />
<component name="nullBean00001" class="xxx.NullBean00001" />
…
– Spring
<beans>
<bean name="nullBean00000" class=“xxx.NullBean00000" />
<bean name="nullBean00001" class=“xxx.NullBean00001" />
…
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
9
コンテナ生成
• コンテナ生成処理
5000
4500
4000
3500
ミリ秒
3000
2500
2000
1500
1000
Seasar
Spring
500
0
1000
2000
5000
コンポーネント数
10000
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
10
コンテナ生成:結果
• Seasar ≒ Spring
• 理由
– リフレクション情報をキャッシュするぶんSeasarの
方が多くの処理を行っていますが、SAXとDOMの
性能差によって吸収されていると思われます。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
11
コンポーネント取得
• 次は、生成したコンテナからコンポーネントを
取得する処理です
• コンテナに登録されている全てのコンポーネ
ントを取得するのに掛かった時間を計測しま
した
– DI・AOPは使用していません
– 単純に、コンテナからコンポーネントを取得する
のみです
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
12
コンポーネント取得
12000
10000
ミリ秒
8000
6000
4000
2000
Seasar
Spring
0
1000
2000
5000
10000
コンポーネント数
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
13
コンポーネント取得:結果
• Seasar >>(10~30倍)>> Spring
– 1000個で1400ms
• コンポーネントを生成するという点ではどちら
も一緒のはずですが、どうして差が出るので
しょうか?
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
14
コンポーネント取得
• 理由
– DIコンテナは、コンポーネントを生成するためにリ
フレクション情報を使用しています
– Seasarはコンテナ生成時にリフレクション情報を
キャッシュしています。コンポーネント生成時には
キャッシュした情報を使用しています
– Springはコンポーネントを取得するときにリフレク
ション情報をキャッシュしています
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
15
コンポーネント取得
• 理由
– Springはコンポーネント取得時にリフレクション処
理を行っているため、遅くなります
– Seasarはコンテナ生成時にリフレクション処理を
行っていますが、SAXとDOMの性能差によって
Springとの差が無くなっています
– そのため、コンポーネント取得時にSeasarの速さ
が際立っています
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
16
リフレクション処理
• では、SeasarとSpringのリフレクション処理は
どれくらい違うのでしょうか?
– リフレクション処理を行うクラスを直接呼び出して
測定しました。
• Seasar: BeanDescImpl
• Spring: BeanWrapperImpl
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
17
リフレクション処理
• リフレクション情報をキャッシュ
14000
12000
10000
ミリ秒
8000
6000
4000
Seasar
Spring
2000
0
1000
2000
5000
コンポーネント数
10000
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
18
リフレクション処理:結果
• Seasar >(3倍)> Spring
– 1000回で1300ms
• 理由
– Seasarはリフレクションキャッシュ処理を独自で実
装しています。SpringのBeanWrapperImplはJDK
のIntrospectorを使用しています。この違いが速
度差となっていると思われます
– キャッシュ実装の違い
• Seasar: HashMap
• Spring: WeakHashMap
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
19
Seasarのコンテナinit処理
• Seasarではコンテナ生成直後にinit処理を行うこと
ができます
– 先ほどまではコンテナのinit処理を行っていませんでした
– init処理を行わない場合は、1度目にコンポーネントを取
得したタイミングで、そのコンポーネントがインスタンス化
されます
• init処理ではsingletonのコンポーネントを作成する
ことができます
– singletonとは、コンポーネント生成は最初1度だけで、そ
の後は最初に生成したコンポーネントを返すこと
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
20
Seasarのコンテナinit処理
• 実際の案件では、アプリケーション起動時に
init処理でsingletonのコンポーネントを生成
した方が効率的です
– Springにはこのような機能はありません
• init処理を含めた場合のコンテナ生成でのパ
フォーマンスを見てみましょう
– Seasar: コンテナ生成 + init
– Spring: コンテナ生成
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
21
Seasarのコンテナinit処理
• コンテナ生成( + init処理)
6000
5000
ミリ秒
4000
3000
2000
1000
Seasar
Spring
0
1000
2000
5000
コンポーネント数
10000
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
22
Seasarのコンテナinit処理:結果
• Seasar ≒ Spring
• 理由
– init処理ではsingletonのオブジェクトを生成して
いるだけなので、それほど時間が掛かりません
– コンテナ作成時の速度はSeasarの方が速いため、
initでのオーバーヘッドをカバーできます
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
23
• では...
• create + initした場合での、コンポーネント取
得パフォーマンスは?
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
24
• create + initした後のコンポーネント取得処理
14000
12000
10000
ミリ秒
8000
6000
4000
Seasar
Spring
2000
0
1000
2000
5000
10000
コンポーネント数
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
25
結果
• Seasar >>>>>>> (60~200倍) >>
>>>>>>>>>>> Spring
– 1000個で1500ms
• 実際の案件ではアプリケーション初期化時に
create + init処理を行っているので、これが
現実のプロジェクトで起こる結果を反映してい
ます
– ただし、コンテナから2回目に取り出すときは、
SeasarもSpringもキャッシュしたオブジェクトを返
すだけなので、差は付きません
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
26
prototype
• 今まではsingletonの場合でした。今度は
prototypeの場合を見てみましょう
– prototypeとは、コンポーネントを取得するたびに
新たに生成することです
• prototypeでも1度目の取得はsingletonと同
様に圧倒的な差が出ます
• 2度目の取得で比べてみましょう
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
27
prototype
• prototypeで2度目のコンポーネント取得
900
800
700
ミリ秒
600
500
400
300
200
Seasar
Spring
100
0
1000
2000
5000
10000
コンポーネント数
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
28
prototype:結果
• Seasar >(3~5倍)> Spring
– 1000個で130ms
• 理由
– Springでは対象となるオブジェクトに加えて
BeanWrapperImplを毎回作っていますが、
Seasarでは対象となるオブジェクトしか作りませ
ん。これが原因の1つかもしれません。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
29
DI (Manual)
• 次はDI処理について見てみましょう
– DIとは、あるコンポーネントが必要とする他のコンポー
ネントを、コンテナがセットしてあげることです(ざっくり)
• 現実的な状況を反映させるため、最初にコンテナ
生成とinitを実行した上で比較しています
– コンテナへコンポーネントを2000個登録しています。2個
で1組です。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
30
DI (Manual)
• コンテナ生成 (Seasarはinitを含む)
3500
3000
2500
ミリ秒
2000
1500
1000
500
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
31
DI (Manual):結果
• Seasar < Spring
– 差は600ms
• 前回コンテナ生成を比較した場合はほぼ一
緒でしたが...
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
32
DI (Manual):結果
• 理由
– 今回2000個のコンポーネントでコンテナ生成した
場合は600ms差が出ています
– この差はリフレクションキャッシュによるものです
– 前回より1000個余分にキャッシュしていることが
今回の600msの差につながっています
• Seasarでリフレクションキャッシュ1000個と2000個を作
成する時間の差が400msでしたので、若干違いますが
ほぼその差と思われます
• 差が大きくないのと、初期化時の処理である
ことを考えると、現実にはあまり問題にならな
いと思います
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
33
DI (Manual)
• 今度は実際にユーザに影響する部分である、
DIしたコンポーネントを取得する処理を見て
みましょう
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
34
DI (Manual)
• DIしたコンポーネントを取得(1000個)
– Manual DI
– singleton
3000
2500
ミリ秒
2000
1500
1000
500
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
35
DI (Manual):結果
• Seasar >>>>>> (100倍) >>>>>
>>>>>>>>> Spring
– 1000セットで2400ms
• DI無しの場合と比べると...
– 今回は2000個から1000個取り出していますが、
1000個から1000個取り出すのと速度は同じです
ので、そのときと比べてみましょう
• DI無しの場合は60倍、今回は100倍。DIする
ことによってさらに差が開いています。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
36
DI (Manual):結果
• 理由
– DIするときに、プロパティに対してリフレクションで
アクセスしています
– リフレクションを行うクラスの性能差が一因と思わ
れます
• リフレクションでのアクセスがどれくらいか見
てみましょう
– 1プロパティへset, getして測定しました
• Seasar: BeanDescImpl
• Spring: BeanWrapperImpl
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
37
リフレクション
• リフレクションでのプロパティアクセス
600
500
ミリ秒
400
300
200
100
Seasar
Spring
0
1000
2000
5000
10000
回数(set, getで1回)
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
38
リフレクション:結果
• Seasar > (4~8倍) > Spring
– 1000回で100ms
• 理由
– BeanDescImplとBeanWrapperImplの差と思わ
れます
• BeanWrapperImplではネストしたプロパティをサポー
トしており、それ関連のオーバーヘッド(文字列操作と
か)が大きいと思われます
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
39
• 次は、prototypeで明示的にDIを指定した場
合の、2度目のアクセスについてです
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
40
• DIしたコンポーネントを取得(1000個)
– Manual DI
– prototype
250
200
ミリ秒
150
100
50
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
41
結果
• Seasar >(3倍)> Spring
– 1000セットで150ms
• DIしない場合でもprototypeでの2度目の取
得は3~5倍の差だったので、DI処理のぶん
更に差が出ると思いましたが、想定したほど
ではありませんでした
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
42
autowire
• 設定ファイルを少しでも少なくするために、
autowireというものがあります
– 設定ファイルにDIを指定するpropertyタグを書か
なくて良くなります
– autowireには幾つか種類がありますが、ここで
は型によるDIを使用しています
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
43
autowire
• DIしたコンポーネントを取得(1000個)
– autowire byType
– singleton
7000
6000
5000
ミリ秒
4000
3000
2000
1000
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
44
autowire:結果
• Seasar >>>>>>>>>>>>>>>
>>>>>>>>>>(300倍)>>>>>
>>>>>> Spring
– 1000セットで6000ms
• Manualでは100倍の差でしたが、Autoにする
と更に3倍の差が付きました
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
45
autowire:結果
• 理由
– autowire時にはDI対象を探すロジックが実行さ
れます
• SpringではDIの度に、毎回コンテナへ登録されている
全てのオブジェクトへアクセスします
– コンテナには2000個登録されていて、1000回DIしているので、
2000 * 1000回コンポーネント定義へアクセスしています。
• Seasarはコンポーネントを登録するときにクラスの型を
キー情報としてハッシュテーブルへ登録しているので、
DIの度に1回のアクセスで済みます
– つまりDIの度にListへ全件アクセスするのか
HashMapへキーでアクセスするのかの差なので、
差が付いて当たり前と言えるでしょう
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
46
• autowireでprototypeの場合はどうでしょう
か?
– 2回目のコンポーネント取得時
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
47
• DIしたコンポーネントを取得(1000個)
– autowire byType
– prototype
3000
2500
ミリ秒
2000
1500
1000
500
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
48
結果
• Seasar >>>> (35倍) >>>>> Spring
– 1000セットで2300ms
• 理由
– singletonと同じで、DI対象を探すロジックの差で
しょう
• singletonほどではありませんが、大きな差が
出ました
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
49
AOP
• AOPとは、バイトコードを操作し もともとの処
理をカスタマイズするもの (ざっくり)
• AOPを掛けたメソッドを実行して、速度差を見
てみましょう
– 今回のAOPは文字列を返すだけの、非常にシン
プルなものです。だからこそAOPのオーバーヘッ
ドがわかりやすいと思います
– 10,000,000回メソッドを実行
• SeasarはJavassist
• SpringはCGLIB (DynamicProxyよりも速い)
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
50
AOP
• AOPを仕掛けたメソッドを実行
3500
3000
2500
ミリ秒
2000
1500
1000
500
Seasar
Spring
0
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
51
AOP:結果
• Seasar >(3~4倍)> Spring
– 10,000,000回で2400ms
• 理由
– Seasarは2.1まではCGLIBで2.2からはJavassist
に変えて、約3倍速くなったことがあります
– CGLIBを使うと殆どチューニングの余地がありま
せんが、Javassistにはチューニングの余地があ
ります
– Seasarではかなりのチューニングを行っているの
で、速くなっていると思われます
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
52
AOP weaving
• AOPを組み込むバイトコード操作を、weaving
と呼んでいます
• このweavingもパフォーマンスに与える影響
があると考えたため、測定してみました
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
53
AOP weaving
• まずは、weavingするクラスを直接呼び出し
て、速度差を比較しました
– Seasar: AopProxy
– Spring: ProxyFactory
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
54
AOP weaving
• AOPのWeaving
140000
120000
100000
ミリ秒
80000
60000
40000
Seasar
Spring
20000
0
1000
2000
5000
10000
回数
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
55
AOP weaving:結果
• Seasar >(3倍)> Spring
– 1000回で8000ms
• 理由
– JavassistとCGLIBでのバイトコードweavingの速
度差と思われます
• AOPのweavingにかかる絶対時間が大きいこ
とがわかります (1000個で8秒!)
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
56
• 次は、登録されているコンポーネントへまとめ
てAspectを仕掛けて、コンテナを生成してみ
ます
• まとめてAspectを仕掛ける機能
– Seasar: AspectAutoRegister
– Spring: AutoProxyCreator
• これらを使ってみました
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
57
• AOP自動登録でのコンテナ生成
300000
250000
ミリ秒
200000
150000
100000
50000
Seasar
Spring
0
1000
2000
5000
10000
回数
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
58
結果
• Seasar >>> (15~60倍) > > > Spring
– 1000個で15000ms
• 理由
– リフレクション情報のキャッシュ
– AOP weaving
• やはり、AOP weavingはDIコンテナの処理の
中では重い部類に入ることがわかります
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
59
• 補足情報
– Springは(今回使用した方法で)AOPを登録する
と、コンテナ生成時にリフレクション情報をキャッ
シュしコンポーネントを生成するようです
• 1度目のコンポーネント取得時に発生していた負荷が
コンテナ生成時に寄っています
• そのぶん、コンポーネント取得時の速度はSeasarと同
じくらいに速くなっています
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
60
まとめ
• DIという同じ技術を実装してこれほどの差が
出るのはかなり驚きです
• ある程度、原因も指摘しているので、この結
果を元にSpringのチューニングに役立てても
らえれば幸いです
• この結果およびテストプログラムはオープン
ソースとして公開する予定です
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
61
本日はご静聴いただき
ありがとうございました。
© Copyright the Seasar Foundation and the others 2006. all rights reserved.
62
ダウンロード

タイトル欄