Bob Lee氏

今知ったのですが、Guice作ってるBob Leeさんはdynaopの作者だったんですね。Bitter EJBの著者の一人というのは知っていたのですが、こちらは知りませんでした。

昔、当時在籍していた会社の勉強会でSwingのフレームワークを作ろうとしていて(参加者数人の構想が全く合わずに挫折(w)、私は画面部品のフィールドにアノテーションで名前をつけてAOPでinjectしたら便利なのではと、AspectWerkzなんかを調べていて、このベンチマークでdynaopの存在を知ったのでした。懐かしい。ちなみに尊敬する先輩(?)からは「クライアントで難しいのはそこじゃない」と言われてて、その後火消しっぽく入ったファイヤーなクライアントプロジェクトでそれを痛感したのでした…。

もう1つ小ネタ、BobさんはTestNGの人に呼んでもらってGoogleに入社したそうです。

そうそうTestNGと言えば、GuiceのプロジェクトメンバーであるHani Suleiman(bileblog)さんの2年前のネタ「TSS Ultimate Summary」は、全てのTSSファンにお勧め。面白すぎです。

Spring2.0.4でパフォーマンスが大幅向上

SpringFrameworkのBeanのインスタンス取得が最大12倍速くなったそうです。

2.0.4リリースアナウンスより引用
Regarding the performance improvements, repeated creation of Spring bean instances is up to 12 times faster in this release than previous versions of Spring 2.0. AspectJ-based weaving performance has also increased by a significant factor.

2.0.3と2.0.4のベンチマーク比較

既にベンチマークのエントリでも速くなったと書きましたが、せっかくなので2.0.3と比較してみました。コードはGuiceベンチマークを流用してます。数値は3回実行した平均値をとりました。

バージョン AOP無しインスタンス取得 AOP有りインスタンス取得
2.0.3 4,650 creations/s 2,037 creations/s
2.0.4 26,140 creations/s 4,360 creations/s
5.62 2.14

このケースでは12倍までは出ませんでしたが、かなりパフォーマンスが向上している事が分かります。AOPAspectJではない旧来の(?)Proxyなので、AspectJにしたらAOP有りの方もより差が大きくなると思われます。
ちなみに突然Springがチューニングした理由として先日お会いしたid:koichikさんが「Guiceが出てきたからだろうね♪」と仰ってましたが、タイミングといい確かにそうなのでしょうね。これまではパフォーマンスの事を言われても「事実上問題にはならない。その部分のパフォーマンスで困ったという事例は聞いた事が無い」というスタンスでしたので。
ともあれ (JW),速くなったのはあくまでBeanのプロトタイプなインスタンス取得処理(とAspectJのweaving)なので,一般的なアプリケーションに与える影響はそれほどではないのではと思います.

ベンチマークを更新しました

SpringFramework 2.0.4で、ちょうどベンチマークに関係する部分のパフォーマンスチューニングを行ったとの事なので(詳しくは次のエントリ参照)、再度ベンチマークを行いました。Seasar2も最新版に入れ替えた上で、セットアップ処理コードを一部変更しています。

計測内容

ベンチマーク結果

- get instance (NO AOP)-
Spring:  24,336 creations/s
Guice:   304,878 creations/s
S2:      26,553 creations/s

- get instance (WITH AOP) -
Spring:  3,967 creations/s
Guice:   45,065 creations/s
S2:      26,232 creations/s

- call service (WITH AOP) -
Spring:  101,971 calls/s
Guice:   843,340 calls/s
S2:      1,270,329 calls/s


- get instance (NO AOP)-
Spring:  24,618 creations/s
Guice:   304,878 creations/s
S2:      25,806 creations/s

- get instance (WITH AOP) -
Spring:  3,766 creations/s
Guice:   42,662 creations/s
S2:      26,448 creations/s

- call service (WITH AOP) -
Spring:  103,727 calls/s
Guice:   959,881 calls/s
S2:      1,271,474 calls/s

前回と比べると、インスタンス取得処理でSpringのチューニング成果が如実に現れていますね。AOP有りも無しもGuiceとの差が10倍くらいまで縮まり*1AOP無しではSeasar2とほぼ並んだと言って良さそうです。
逆にSpringはAOPインスタンス取得や実行の遅さが際立ちますが、AspectJを使うとだいぶ数値が違ったりするんでしょうか。やはり試さないといかんかも。
それと、S2がAOP有り無しでパフォーマンスが変わらないのが驚きです*2。weaveするタイミングが他のコンテナと違うとかなのでしょうか?でも実行は一番速いから、単純に遅延してるとかでは無さそうだし…。

実行したベンチマークコード、変更点などは

以下のwikiに置きました。
Guice, Spring, Seasar2 ベンチマーク(第2回)
なお前回はページタイトルがSpringではなく"Sring"になっているという大変恥ずかしい事になっていたので(しかしオレンジニュースに載ったので変えたりデリゲートする勇気が無かった)、今回は気をつけました:-)

wikiなので、改善点や追加、情報などありましたら書き込んで頂けると嬉しいです。

*1:「まだ10倍かよ!」という意見もあると思いますが、使う立場の私個人としてはこの辺の数値をもともとあまり気にしていないのと、ライフサイクルサポートなど機能が異なるので、Spring頑張ったなぁというのが素朴な感想です

*2:計測コード間違ってなければ…

Spring勉強会での発表資料を公開しました

先日行われたJapan Springframework User Groupの勉強会Guiceについてライトニングトークした資料を公開しました(アップロードありがとうございます>岩永さん)。
ブログを読まれている方には新しい情報は無いと思いますが、よろしければどうぞ。
発表資料のダウンロードはこちらから
また、PowerPointを閲覧できない方や落とすのが面倒な方は、SlideShareでどうぞ。
SlideShareの資料※何故かFAQの"A"が"C"になっちゃってますが、それ以外は上記資料と同じ内容です。

ブレースとdo/endの結合度

ここ1,2週間くらいちょっとRubyを触りだしているのですが、一昨日勉強会で知った事があります。

p [1,2,3].map { |v|
  v + 1
}
p [1,2,3].map do |v|
  v + 1
end

これまでブレースとdo/endは常に置き換え可能なものなのかと思っていたのですが、上記を実行すると

[2, 3, 4]
[1, 2, 3]

となるんですね。はっし〜〜さんによると結合順序から来る現象らしいのですが、まだ理解できていません。更によく分からない事に、

w = [1,2,3].map do |v|
  v + 1
end
p w

という風に代入すると

[2, 3, 4]

になるんですよね。うむむ。

…とブログ書きながら調べてみたら、この説明でなんとなく分かりました。最初の例だと「mapメソッドの戻り値」がpに渡されて、次の例だと「何もしないmap」と「doのブロック」の2つが渡されているんですね。
という事でブロック内で文字列を出してみました。

p [1,2,3].map { |v|
  p "hello1"
  v + 1
}
p [1,2,3].map do |v|
  p "hello2"
  v + 1
end

結果

"hello1"
"hello1"
"hello1"
[2, 3, 4]
[1, 2, 3]

おー、やはり2番目の例はブロックが実行されていなかったんですね。
しかしまだ分かっていないのが3番目の例です。代入する時左辺が1つだから、右辺も1つになるようにまとめて評価される、みたいなイメージなんでしょうか???
うーむ、先は長そう。id:zuisenerさん曰く「日本=Rubyの国」らしいので、何とか使えるようになりたいものですが。ちなみに今はアセンブラJavaしか使えません(と言ってもアセンブラは中学高校浪人までなのでかなーり忘れてしまい、実質Javaのみでした)。

第2回勉強会に参加してきました

昨日はJSUG第2回勉強会に参加してきました。
スピーカである豆蔵の麻野さん、ATL Systemsの船戸さん、arclampのゆーすけさん、ありがとうございました&お疲れ様でした。
私もid:zuisenerさんに続いて、ライトニングトークGuiceについてちょっと喋らせてもらいました。5分の持ち時間なのに、タイマーを拒否した結果(プレッシャーに弱いからw)、10分強になってしまいましたが…。すみませんでした>運営者の皆様、後の発表だった船戸さん、鈴木さん

その後は懇親会でjava-jaの方達とも合流して、40人くらいで飲みました。お話してても最後まで名前が分からなかった方が何名かいらして申し訳ないのですが(最初にちゃんと訊けって話ですね)、ジャスミンソフトの代表の方、前述の麻野さん、DSASの会社の方、ゆーすけさん、よういちろうさん(今度遊びに来てね。ついでに趣味でコーディングしてっても良いですよw)、河村さん(一昨日に続きお世話になりました)、電話のお仕事の方、id:koichikさん、id:t_yanoさん、櫻庭さんjFDの方(後ろから覗き込んで「懐かしー」って言ってただけですみません。中学高校時代はFD命でした)、seasar2な方(たぶんブログ拝見してる方のどなたかだと思うのですが名前分からず…)、などなど色々な方々とお話しました。いやー楽しかった。

そして帰りは電車が同じだったkoichikさんと2人でお話して得した気分。Springが速くなってS2もチューニングしたとの事なので、近い内に例のベンチマークを更新したいと思います。

あ、発表に使ったGuiceパワポも今日か明日には公開します。

Spring Java Configuration(1)

Guiceと Springframeworkの最大の違いはDIやAOPの設定をJavaコードで書くかXMLで書くかですが、実は(?)SpringでもJava コードによる設定の記述手段が用意されています*1。今回はその"Spring Java Configuration"を簡単に見てみます。なお、まだマイルストーン1なので、用意されているというよりは準備されている、もしくは計画中と言う方が適切かも知れず、今回試す内容も今後のリリースで色々変わる可能性があります。

Spring Java Configurationの最も基本的なサンプル

まずinjectされるサービス。インタフェースとその実装です。

public interface Service {
  String getResponse(String msg);
}

public class ServiceImpl implements Service {
  public String getResponse(String msg) {
    return "Re: " + msg;
  }
}

次に、inject場所であるClient。後でも書きますが、Guiceの場合と異なり、厳密な意味でのPOJOです。

public class SpringClient {
  private Service service;
 
  public SpringClient(Service service) {
    this.service = service;
  }
   
  public void execute() {
    System.out.println(service.getResponse("Hello"));
  }
  /** テスト用 **/
  Service getService() {
    return service;
  }
}

そしていよいよ設定するコードです。GuiceはModuleを実装したクラスでしたが、Spring Java Configurationではアノテーションで設定コードである事を示します。

import org.springframework.beans.factory.annotation.Bean;
import org.springframework.beans.factory.annotation.Configuration;
import org.springframework.beans.factory.annotation.Scope;

// 設定コードである事を示すアノテーション(色々オプションもありますが今回はデフォルトで使用)
@Configuration
public class SpringConfiguration {
  // XMLの<bean>定義に相当する事を示すアノテーション
  @Bean
  public Service service() {
    return new ServiceImpl();
  }
  // こちらではプロトタイプを指定(指定しないとシングルトンになるのは通常のSpring設定と同じ)
  @Bean(scope = Scope.PROTOTYPE)
  public SpringClient client() {
    return new SpringClient(service());
  }
}

@Beanをつけたメソッドがファクトリメソッドになるという事ですね。アノテーションが無ければ設定の為とは思えないような、普通のJavaコードになっています。
最後にコンテナを生成する起動部分。

public class SpringBootStrap {
  
  public static void main(String[] args) {
    // JavaConfiguration用のコンテナを生成
    AnnotationApplicationContext context
        = new AnnotationApplicationContext(SpringConfiguration.class);
    
    // コンテナからBeanを取得(このコードはXMLの場合と同じ)
    SpringClient client = (SpringClient)context.getBean("client");
    client.execute();
    
    SpringClient anotherClient = (SpringClient)context.getBean("client");
    // 自前のnewではなくちゃんとコンテナ管理されている事を確認する為、PrototypeとSingletonのインスタンスをチェック
    System.out.println("client == anotherClient : " + (client == anotherClient));
    System.out.println("client.getService() == anotherClient.getService() : "
        + (client.getService() == anotherClient.getService()));
  }
}

実行結果

Re: Hello
client == anotherClient : false
client.getService() == anotherClient.getService() : true

ちゃんとプロトタイプ指定したインスタンスは別に、シングルトン指定(デフォルト)のインスタンスは同じとなって実行されました。

感想

Guiceの手法と比べて際立つのは、Spring Java ConfigurationはアプリケーションのPOJOフレームワークに依存させない(=汚染しない)ようにするという方針です。Guiceはinjectされる場所に@Injectアノテーションをつけるので、そのクラスやインタフェースがGuiceに依存してしまいます。しかしこのSpringの手法はXMLの場合同様、完全に外部からinject対象やinject場所を指定するので、アプリケーションのオブジェクトはクリーンなままです。
逆に言うとどこにinjectされるかが対象コードを見ただけだと分からないというデメリットもありますが、そこはIDEプラグインがいずれ解決してくれそうなので、実際にはあまりデメリットにならない可能性が高いです。

ただ個人的な感想としては、設定コード(@Beanがついたメソッド群)が普通のJavaコードなのでコンテナやリフレクションの「臭い」がしない為、逆に気持ちが悪いというか、直感的に分からないというモヤモヤ感を感じてしまいました。普通にメソッド呼び出してnewしてる筈なのになんでシングルトンになるんだ、とかそういうところです。AOPもある意味同じなのですが、処理が追加されるのではなく、書いてある事と動作が(少し)違ってくるというのがどうも…。まあ慣れの問題も大きいと思うので、ちょっと経ったら「やっぱりこっちの方が良い」とか言い出すかも知れませんが。

という事で、今回は非常に単純な例を試してみました。XMLの設定とも共存できる(同時に使える)ので、必要に応じてどちらかを選んだり、上手く使い分けて両方使ったりが簡単にできそうな感じです。そいれでは次回はAOPを試してみたいと思います。

*1:Groovyで書くといった手段もあるようですが、全然知らないのと、あまり広くは受け入れられそうにない気がするので省略します