groovy now and future
Embed Size (px)
DESCRIPTION
Groovy new feturesCall site optimization,AST Translation,mixin, etc.TRANSCRIPT

Groovyなう&ふゅーちゃーそれからのGroovy
GCR 16thNTTソフトウェア上原潤二
2009/1/15

Groovyイン・アクション(GinA)• 国内初のGroovy解説書(翻訳)• “バイブル本“• 対応Groovyバージョン
–原書はGroovy 1.0ベース–サンプルコードをGroovy 1.5.4で動作する
ように緻密に修正。–Groovy 1.0より後に実装された機能につい
ては基本的には追記しない。ので本発表ではそこを説明します

Groovy年表2003 2004 2005
2006 2007 200812月▲
1.5.0
2月▲
1.5.4
2月▲1.0
4月▲
1.1b2
12月▲
1.0b8
8月 3月▲
1.0b4JSR化
2月▲
1.0b10JSR EA
7月▲
1.0 JSR-6
12月▲1.5.71.6-rc1
本日の対象
Grails 0.5.6 Grails 1.0 1.1b2
1.1b1
GinA本対象

Groovy 1.1と1.5の関係•1.1b1, 1.1b2, ・・・, 1.1-RC1,
ときて1.5–Java “5”の機能取り込みと、機能が⼤幅アップしたから
–1.1正式版は存在しない。–本資料では、1.1および1.5の新機能を
のマークで表⽰–ちなみに1.6機能は で。
From 1.1・1.5
From 1.6

Groovy 1.1=1.5の主な機能• Java 5対応
– アノテーション利⽤– enum(ただし、インナークラスになるenumは×)– 可変個数引数– static import– Generics– 共変の戻り値
• ⾔語仕様– エルビス演算⼦– for(;;)形式ループ– 名前付き引数で関数呼び出しの括弧省略
• ジョイントコンパイラ• Expando Meta Class (EMC)• 性能向上,bugfix,改良,etc,etc, From 1.1・1.5

Groovy 1.6の主な機能• ⾔語仕様
– マルチ代⼊(多重代⼊)– 分岐しててもreturn省略可– AST変換– Groovyでアノテーション定義
• 動的な振る舞いに関するもの– JavaインスタンスごとのMetaClass– EMC DSL– 実⾏時Mixin
• ライブラリ: Swing Builderの改善• ツール: Grape• 性能向上• bugfix,改良,etc,etc
From 1.6

ベンチマーク

ベンチマーク!•The Computer Language
Benchmarks Game–Aka “shootout”–マイクロベンチマーク
•抜粋して実施

その前に• 速度にこだわるべき?
–マシンも速くなってるし–Groovyであることで遅いなら、
ボトルネックをJavaで書き直せばよいそれはものすごく簡単
• 処理系速度は体感速度に対して⽀配的とは限らない–ライブラリやIOの性能が⽀配的な場合も
• でも–Groovy同⼠なら⾯⽩かろう

Groovy 1.1beta3を1とした速度⽐

Groovy 1.5.6を1とした速度⽐

感想(感覚的なものです)•Groovy 1.6でメソッドコールが爆
速に(理由は後述)•つってもJRubyと同等(かやや上)•JVM上の⾔語の共通の特徴として
–起動は遅いっすよ–メモリは⽐較的⾷うっすよ

From 1.1・1.5

From 1.1・1.5
エルビス演算⼦(Elvis Operator)•三項演算⼦
A ? B : Cは次と等価if (A) { B } else { C }
•エルビス演算⼦A ?: Bは次と等価if (A) { A } else { B }
※GCCの独自拡張に由来

エルビス演算⼦(Elvis Operator)

ジョイントコンパイラ• GroovyとJavaコードが相互依存してい
てもコンパイル可能にclass A {
B b;}
class B extends A {} • 内部では、Groovyクラスのスタブ(メ
ソッド本体を削除したもの)を⽣成してからJavaコンパイラを起動する
• groovycの-jオプションで実⾏From 1.1・1.5

ExpandoMetaClass (EMC)•プロパティにクロージャを代⼊する
とメソッドの様に扱えるString.metaclass.hello = { println “hello”+delegate }“world”.hello() // helloworldが出⼒
•簡便な動的メソッド定義。•prototypeと同様だが、Groovyでは
クラスベースOOPと⼆本⽴て。EMCは後付け。
From 1.1・1.5

From 1.6

Call Siteキャッシュ,Call Site最適化• 性能向上の主因
–「メソッドコールが遅い」←Groovyの遅さは⼤半これで説明。
• 動的メソッド呼び出しに先⽴つメソッド探索をキャッシュ–動的⾔語で効果が⼤–JRubyが先達–アイデア⾃体は古くからある
• JVM上で極めて有効(後述) From 1.6

動的メソッド呼び出し概念フロー
x = new X()x.foo()
呼び出し側クラス
メソッド探索
Class X {
def foo() {
}
GroovyクラスX
Pogo呼び出し
Pogo …Plain Old Groovy Object

動的メソッド呼び出し概念フロー
x = new X()x.foo()
呼び出し側クラス
メソッド探索
Class X {
}
GroovyクラスX
メタクラスのメソッドの呼び出し
‘foo’ = { }
Xのメタクラス

問題はメソッド探索のコスト• 探索時に以下の判定を⾏う必要がある
–Pojo/Pogoのメソッド?–メタクラス定義メソッド?
•クラスごとのメタクラス?•インスタンスごとのメタクラス?
–プロパティ参照か?–コンストラクタ呼び出しか?–インタセプト可能か?–Staticかどうか?
:
明らかに重い

Call Site Cache (1回⽬)
x = new X()x.foo()
呼び出し側クラス
メソッド探索
Class X {
}
GroovyクラスX
CallSite
‘foo’ = { }
Xのメタクラス
PogoMetaClassSiteのインスタンスを保存
※CallSiteはSoftReferenceで保持されるので
メモリがなくなってきたら解放される

Call Site Cache (2回⽬)
x = new X()x.foo()
呼び出し側クラス
Class X {
}
GroovyクラスX
CallSite
‘foo’ = { }
Xのメタクラス
保存されたPogoMetaClassSiteのインスタンスをcall()
※実際には呼び出しが複数あるので配列CallSiteArrayで保持※意味が動的に変わったらCallSiteのインスタンスを差し替える
(自分で自分を置き換える。コード的にはこれがやや面白い)

Call Site最適化まとめ• (1)メソッド探索コストを2回⽬以降削減• (2)JITによるインライン展開の道を開く
– マシン語レベルでほんとに展開されているか私は確認してません(Open JDKで確認可)がほんとなら⾮常に効果が⾼い(連鎖的展開を導く)
– インライン展開に関してHotSpotは極めて強⼒• アグレッシブにやって状況変わったら
Deoptimization(最適化戻し)• (3)特定のメタメソッドについて、⾃分専⽤
の速いCall Siteを⽣成– DefaultGroovyMethod#additionals参照

Grape:依存クラス⾃動ダウンロード• Maven/RubyGemsみたいなリポジト
リベースモジュール管理–Apache Ivyベース–Groovyレベルで標準基本機能として実現–必要なクラス/Jarを実⾏時にも取ってこれる–依存関係があれば連鎖的にJarを取ってくる–Grails Pluginのような、実⾏時モジュール
機構は含まない• 効⽤: Groovyスクリプト配布が容易に
From 1.6

From 1.6
Grape使い⽅• アノテーション
• メソッド呼び出しGrape.grab(group:'org.jidesoft', module:'jide-
oss', version:'[2.2.0,)')• ʼgrapeʼコマンド
import org.junit.*@Grab(group = 'junit', module='junit', version='*')public class GrapeTestSub {
String testMethod() {Assert.class.name
}}

JavaインスタンスごとのMetaClass• 例:
s = “xxx”;s.metaClass.a = { println "hoge"}s.a()// hogeが出⼒– Groovyならもともと可能– 「JavaクラスごとのMetaClass」は元々あった
• 参考:クラスごとのMetaClassString.metaClass.x={println “hoge” }“abc”.x()// hogeが出⼒
From 1.6

AST変換• AST …抽象構⽂⽊
– コンパイル中にコンパイラ内で作られる中間データ構造
• AST変換– 指定したアノテーションが与える規則に従って、
コンパイル時にASTを変換することで、⾔語機能を加⼯・追加する
– デザインパターン指向プログラミング– コンパイラソースを修正せずに⾔語機能追加。– 定義は結構難しそう(Visitorで)– 利⽤は簡単
From 1.6

AST変換の例プロパティの更新伝播(java.beans. PropertyChangeSupport使⽤)
@Bindable/@Vetoable
newなしで「Integer(5)」と書けばインスタンスが⽣成。(未実装?)
@Newify
packageスコープの指定。フィールドについては動いているようだがクラスについては?
@PackageScope
カテゴリのメソッドをコンパイル時にmixinする
@Category / @Mixinプロパティの怠惰(Lazily)な初期化@Lazy
委譲(delegation)パターンを透過的に実現
@Delegateインスタンス⽣成後の変更を禁⽌@Immutableクラスをシングルトンに変換@Singleton
説明アノテーション

AST変換: @Singleton@Singleton class Foo {def hello() { println "hello" }
}Foo.instance.hello() // シングルトン参照a = new Foo() // newでインスタンス⽣成不可ERROR
java.lang.RuntimeException: Can't instantiate singleton Foo. Use Foo.instance

AST変換: @Immutable@Immutable final class Foo {String x, y
}a = new Foo("a","b")a.x = “hoge” 値の変更ERROR
groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: x

AST変換: @Delegateimport java.util.concurrent.locks.*class LockableMap {@Delegate private Map map = [:]@Delegate private Lock lock = new ReentrantLock ()
}res = new LockableMap()res.lock () Lockとして振舞うtry {res.a = 0 Mapとしても振舞う
} finally {res.unlock ()
} ※MapとLockをimplementsもする

AST変換: @Lazyclass LazyTest {@Lazy List s1 ={println "s1 initializing"; [1,2,3] }();List s2 = {println "s2 initializing"; [4,5,6] }();
}x = new LazyTest()// s2 initializing s1の初期化は遅延されるprintln x.s1 s1への参照で初期化実⾏// s1 initializing// [1, 2, 3]が出⼒されるprintln x.s2// [4, 5, 6]が出⼒される

AST変換: @Category/@Mixininterface Mixed {}
@Category(Mixed) class CatClass {def hello() { println "hello" }
}
@Mixin(CatClass)class MyClass implements Mixed {}
x = new MyClass()x.hello()
※interface Mixedの意義が不明。なんらかの処理上のマーカー?

動的なMixinメソッド呼び出し
String.mixin Dateassert "a".getTime() == 1231768545451
From 1.6

整理:さまざまな機能注⼊の⽅法
クラス/インスタンス
実⾏時クロージャ群EMC DSL
クラスオーバーライドはコンパイル時に⾏われるが、処理内容は実⾏時に決定可
メソッド呼び出しに対応する処理
MOP(invokeMethod,methodMissing)
インスタンス実⾏時メソッド呼び出しの前後の処理
インターセプタ
インスタンス実⾏時クロージャExpando
実⾏時
コンパイル時コンパイル時実⾏時
Groovy処理系のコンパイル時
実⾏時(use⽂実⾏時)コンパイル時コンパイル時
注⼊タイミング
クラスメソッド群継承
クラスメソッド群?Delegateアノテーション
クラスメソッドメソッドを書く
クラスメソッド群Mixinアノテーション
クラスstaticメソッド群Category/use
クラスメソッド群Mixinメソッド呼び出し
クラス(Java)staticメソッド群DefaultGroovyMethods.java(DGM)
クラス/インスタンス
クロージャExpandoMetaClass(EMC)
対象注⼊単位名称
1.11.5
1.6

マルチ代⼊(多重代⼊)def (a, b) = [1,2]
def (int i, String j) = [1, 'Groovy']
def a, b(a, b) = func()
• 要素が余ったら捨て、⾜りなかったらnullで補填
From 1.6

分岐しててもreturn省略可def even(x) {
if (x % 2 == 0) { true } else { false }}assert even(2) == trueassert even(1) == false
• ちなみにwhile⽂の最終評価式は値にはならないようだ
From 1.6

EMC DSL• 逐次プロパティに代⼊するのではなく
⼀気に定義する。String.metaClass.define {foo { s-> println s + delegate }bar { println “hello” }
}“world”.foo(“hai”) // haiworldが出⼒“abc”.bar() // helloが出⼒
From 1.6
※効用: 逐次代入だと、途中で未完成状態のクラスが生じるから?

まとめ

まとめ: Groovyの⽬指すもの• Java 7がしょぼくなった今、Groovyは
⾔語機能拡張のメルティングポット–Java 7のクロージャ(案)は、⼿ひどく複雑
そうだったので、正直ほっとしている。• “Java is COBOL”?そうじゃあない。
“Java is C”なんだ。–ポイントは「住み分け」
• Groovyは、JVM上にクラスをローディングするための別記法。
• 「Java+Groovy」は2つで⼀つの技術

参考文献• http://glaforge.free.fr/weblog/index.php?itemid=241• http://www.nabble.com/DGM-and-GDoc-
to14985262.html#a14985262• http://blog.headius.com/2008/05/power-of-jvm.html• http://glaforge.free.fr/weblog/index.php?itemid=99• http://glaforge.free.fr/weblog/index.php?itemid=256• http://www.infoq.com/jp/articles/groovy-1.5-new‘• http://shootout.alioth.debian.org/• http://d.hatena.ne.jp/masanobuimai/20081224/1230123639• http://docs.codehaus.org/display/GROOVY/Grape• http://docs.codehaus.org/pages/viewrecentblogposts.action?k
ey=GROOVY• http://shemnon.com/speling/2008/04/bindable-observable-
properties.html