Saturday, March 16, 2013

GBench のチュートリアルが登場

オープンソースプロジェクトにとって、最も重要なものの一つがドキュメントなのは疑いようもありません。例えば Groovy プロジェクトは数日前にドキュメント向上へ注力することを宣言しました: http://groovy.329449.n5.nabble.com/ANN-Documentation-effort-and-site-redesign-td5712875.html

というわけで、僕も Groovy のベンチマーク用モジュール、 GBench のチュートリアルを書きました: https://code.google.com/p/gbench/wiki/BenchmarkBuilderTutorial

ちょっとばかり短いですがこのドキュメントがみなさんの Groovy ライフに少しでも役立てばと思います。

Wednesday, May 9, 2012

InvokedynamicでGroovyライフをリスタートしよう

Groovyは5/7にリリースされたバージョン2.0 beta 3でinvokedynamic (indy)に対応しました。これはindyであなたのGroovyライフをリスタートする方法をお見せするだけの短い投稿です。Indyはダイナミックなメソッド呼び出しのためのJava SE 7の新たなバイトコード命令です。もしあなたがindyについて知らないのであればJSRを読んでください。

さぁ、では始めましょう。まずはindyに対応したバージョンのGroovyをインストールしてください。現時点(2.0 beta 3)ではindyはデフォルトで対応されていないためです。次のいずれかの方法が選べます。

* アーカイブをダウンロードしてlib/内のgroovy.jarあるいはembeddable/内のgroovy-all.jarの代わりにindy/にあるgroovy-indy.jarかgroovy-all-indy.jarを使う。

* indyを有効にするオプションを使ってソースからビルドする

ant install -DuseIndy=true -DskipTests=true

2番目に、あなたの目的に応じた方法で次のようにindyを有効にするオプションを付けてコンパイルします:

* groovy

groovy --indy YourScript.groovy

* groovyc

groovyc --indy YourScript.groovy

* GroovyShell class

import org.codehaus.groovy.control.CompilerConfiguration

def conf = new CompilerConfiguration()
conf.optimizationOptions.indy = true
def shell = new GroovyShell(conf)
shell.evaluate(/* your script */)

これだけです! それでは良いGroovyライフを!

Friday, May 4, 2012

Exploring JavaFX 2 - アプリケーションパラメタへのアクセス

JavaFXはApplicationクラスオブジェクトからアプリケーションパラメタにアクセスする方法としてApplication.getParameters()を提供します。例えば、ユーザがGNU形式でオプション(名前付きパラメタ)に幅と高さ、引数(名前なしパラメタ)にメッセージを指定すれば:
java AccessApplicationParametersDemo --width=200 --height=100 hello
アプリケーションは次のようにパラメタを受け取れます:
public class AccessApplicationParametersDemo {

    public static void main(String[] args) {
        Application.launch(UsingGetParametersApplication.class, args);
    }

}
public class UsingGetParametersApplication extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Map opts = getParameters().getNamed();
        System.out.println("width=" + opts.get("width"));
        System.out.println("height=" + opts.get("height"));
        
        List args = getParameters().getUnnamed(); 
        System.out.println("message=" + args.get(0));
    }

}
但しgetParameters()は今のところ(v2.2-b06)オプションのパーサが貧弱で使い勝手がよくありません。省略オプションやPOSIX形式に対応してなく、次の形式のどれも扱えません:
-w=200 -h=100
--width 200 --height 100
-w 200 -h 100
getParemters()をカスタマイズすればいいでしょうか?しかし実装クラスへのアクセスが中でハードコードされていて、メソッドがfinalだと聞いたらどうでしょう?つまりgetParameters()から直接オプションや引数を取得するのは今のところ良い選択ではありません。getParameters()には運搬役に徹してもらい、主な作業はApache Commons CLIに任せるのがお勧めです:
public class UsingCommonsCliApplication extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Options options = new Options();
        options.addOption("w", "width", true, "");
        options.addOption("h", "height", true, "");
        CommandLineParser parser = new PosixParser();
        CommandLine cmd = parser.parse(
            options, 
            getParameters().getRaw().toArray(
                new String[getParameters().getRaw().size()]
            )
        );
        System.out.println("width=" + cmd.getOptionValue("width")); 
        System.out.println("height=" + cmd.getOptionValue("height")); 
        System.out.println("message=" + cmd.getArgList().get(0));
    }

}
この問題に対するJavaFXチームからの回答は"将来のバージョンでは検討可能"ということなのでそのうち解消されるかもしれません。そもそもこのような機能はパラメタを固定された形式で受けとるAppletやJWSでのみ必要とされるはずなので、JavaFXはAppletApplicationやJWSApplicationのような各環境用のクラスを持ち、それらにのみこの機能を持たせる方が良さそうです。

Tuesday, March 20, 2012

正しいGroovyベンチマーク

GroovyのようなJVM上に乗っている言語のベンチマークは難しく、安易に行うと誤った結果を出すことになります。例えばStringBuilderとStringBufferの比較するため次のようなプログラムを書いたとします。StringBufferは同期の為にStringBuilderよりも遅いことがよく知られています:
def n = 100 * 1000
def at, bt

bt = System.nanoTime()
n.times {
    def sb = new StringBuilder()    
    sb.append('foo')
    sb.append('bar')
    sb.append('baz')
}
at = System.nanoTime()
println((at - bt) / n) 

bt = System.nanoTime()
n.times {
    def sb = new StringBuffer()    
    sb.append('foo')
    sb.append('bar')
    sb.append('baz')
}
at = System.nanoTime()
println((at - bt) / n) 

しかし期待とは異なりStringBufferがStringBuilderよりも2倍以上も速い結果になりました(Groovy 1.8.6、JVM 1.7.0_04-ea Server VM)。最適化により同期のコストがなくなったとしても同等以下のはずであり、この計測は完全に失敗しています:
StringBuilder   1947.3 (2.76)
StringBuffer     703.1 (1)

StringBuilder同士で比較すれば失敗はより明らかとなります:
StringBuilder #1   2030.68 (4.85)
StringBuilder #2    418.47 (1)

何が問題だったのかは実行時間の推移を見るとわかります。1回目の計測中に最適化が行われ、2回目は最適化がある程度完了した状態から開始されています。そのため1回目は2回目より不利となります。また、2回目の計測中に飛び出ている数値があります。これはガベージコレクションによるものですが、この時に回収されるゴミは1回目の計測中に出たものを含みます。この点では逆に2回目が1回目より不利となります。つまり正しいベンチマークとは同じスタートラインに立たせることであり、そのためには計測前に最適化を完了させメモリを掃除しゴミ箱を空にしておく必要があります。


これらの問題を自身で解決するのもよいですが、幸運なことにGroovyにはベンチマークフレームワーク、GBenchがあります。GBench 0.3.0はあなたに代わってこれらの面倒な課題を解決し正しい結果を出す機能を持っています。GBenchを使って書き直したのが次のコードです。measureCpuTimeをfalseにしCPU時間の計測を無効にすると実行時間のみの簡潔なレポートを受け取れます:
import gbench.*

new BenchmarkBuilder().run(measureCpuTime:false) {
    'StringBuilder' {
        def sb = new StringBuilder()        
        sb.append('foo')
        sb.append('bar')
        sb.append('baz')
        sb.toString()
    }
    'StringBuffer' {
        def sb = new StringBuffer()        
        sb.append('foo')
        sb.append('bar')
        sb.append('baz')
        sb.toString()
    }
}.prettyPrint()

このコードの実行結果は次のようになりました。差がそれ程ないのは私の環境がServer VMであり、ロック周りが最適化される為です。ロックの最適化ついてはJeroen Borgersの記事、Java 6のスレッド最適化は実際に動作しているのか?が参考になります:
StringBuilder   244 (1)
StringBuffer    265 (1.08)

最適化を無効にした(-XX:-DoEscapeAnalysis -XX:-EliminateLocks -XX:-UseBiasedLocking)結果は以下のとおりでした:
StringBuilder   242 (1)
StringBuffer    310 (1.28)

Sunday, December 18, 2011

メールからエンジニアの活動パターンを調査する in Groovy

これは Ariel Advent Calendar 2011 の記事です。

僕が以前所属していたアリエル・ネットワークではほとんどの情報がメールで共有されていました。面倒なミーティングに招待されたことや、誰かが typo まみれの恥ずかしいコードをコミットしたことも全てメールで届きます。それはログであり、誰が、何時、何をしているかがそこからわかります。

エンジニアの活動について知りたいならば最も適切な材料になるのは ITS (Issue Tracking System) から届くメールです。問題の報告、仕様の記述、進捗や調査内容の共有、質問と回答などなど、事あるごとにメールが飛びます。今回はそれらのメールの送信時間から何がわかるのか見てみたいと思います。使うのは手元に残っている 2007 - 2009 の12月のメールです。mbox で保存されている各月ごとのメールと次の Groovy スクリプトを使って、時間帯ごとの送信回数をグラフで表示してみます(DateヘッダがGMTだったので実際はJSTに変える処理が必要だったけどそこは省略):
def mbox = mboxName as File
mbox.withReader { r ->
    def acts = new int[24]
    while (r.ready()) {
        (r.readLine() =~ /^Date:.+\s(\d{2}):\d{2}:\d{2}\s/).with { m ->
            if (m) acts[m[0][1] as int]++ 
        }
    }
    acts.eachWithIndex { c, h ->
        println(String.format("%2d %s(%d)", h, '*'*(c/10), c))
    }
}

Dec 2007:
 
 0 **(27)
 1 **(20)
 2 (5)
 3 (5)
 4 (2)
 5 (0)
 6 (0)
 7 (0)
 8 (0)
 9 (1)
10 *************(132)
11 *********************************(337)
12 **************************************(384)
13 ***************(158)
14 ****************************(286)
15 *******************************(312)
16 *******************************(314)
17 *************************************(374)
18 ***********************************(351)
19 *******************************(315)
20 **********************(228)
21 ********************(203)
22 ****************(166)
23 *****(59)
 

Dec 2008:
 
 0 (5)
 1 (7)
 2 (0)
 3 (0)
 4 (1)
 5 (0)
 6 (0)
 7 (0)
 8 (0)
 9 *(17)
10 ***********(119)
11 ******************************(304)
12 ************************(245)
13 ***********(119)
14 ****************************(288)
15 *******************************(311)
16 *************************(250)
17 ***************************(275)
18 **********************(226)
19 *****************(171)
20 *********(99)
21 *****(55)
22 *******(77)
23 *(19)
 

Dec 2009:
 
 0 ******(68)
 1 **(26)
 2 **(29)
 3 **(21)
 4 (7)
 5 (8)
 6 (5)
 7 (2)
 8 (5)
 9 *******(75)
10 **********************************(349)
11 **************************************************************************(749)
12 ****************************************************************************(766)
13 **************************************************(502)
14 ***********************************************************(598)
15 *************************************************************(615)
16 ****************************************************************(642)
17 *********************************************************************(698)
18 ***************************************************************(634)
19 **********************************************************(584)
20 **********************************(343)
21 ***********************(235)
22 *************(136)
23 *************(139)
 

このグラフから次のことが見て取れます:

1. 10時 - 24時までが主な活動時間: 今は朝7時とかに出社するよくわからないグループもいるみたいですが。

2. 10時台になると一気に活発になる: 多くの人は 10時 - 11時に出社するようです。そう、アリエルは11時までに出社すればOKです。ギリギリに来る人が多かったですね。

3. ランチは13時台: 自分が早く出社してお腹が空いているからといって早めに誘ってしまうと、12時台のピークを奪うことになりますから止めましょう。

4. 午後の活動のピークは17時前後: ランチ後の睡眠欲に打ち勝ち(あるいは充足し)、ノッている時間帯です。この時間帯にミーティングが入ってしまうと生産性が非常に落ちます。

5. 20時 - 22時 にかけて人が帰宅していく: ただし、アリエルは15時以降ならいつでも帰ってよいことになってるし、帰りづらい雰囲気は皆無です。僕も明るいうちに会社出て遊びに行ったりしてました。逆に忙しい時は自宅で朝っぱらから夜中までとかもやってましたが。自由を制する力が求められます。

6. 2008年と比較すると2009年は倍増: 会社が急激に成長したようです。グラフの形があまり変わっていないのは仕事と一緒に人もどんどん増えた結果でしょうか。コアチームのメンバーはほとんど増えなかったですけど。

見落としていることもあるかもしれません。でも少なくともメールの送信時間だけでこれだけのことはわかるわけです。ここに人やissueの属性が加わればもっといろんなことがわかります。

このようにログを取っていてそれを利用できる会社なら、無駄な報告作業が減るかもしれませんね。

Tuesday, December 13, 2011

G5、それはGroovy使いの為のスライドショーツール

これは G* Advent Calendar の 12/13 (13日目) の記事です。

タイトルにあるとおり、 Groovy でスライドショーできるツール、 G5 をリリースします(おそらくクリスマスまでには!)。インスパイアされたのが HTML/JavaScript/CSS でスライドショーする S5Clojure でスライドショーする L5 なので、 G5 と名づけました。ちなみに L5 の作者の深町さん("はてな"の人)はアリエル時代の同僚で、S5 より L5 を先に知りました。このツール、作ろうかなと思ってから1年くらい経っていて、今回の Advent Calendar のネタとしてようやく手を付けたので、まだ数時間しか費やせてません。完成度は察してください。

まずはシンプルな例です:
new g5.SlideBuilder()({s{b("Look Ma, one line!")}})()

これで次のようにスライドショーが開始されます。今のところ Apache FOP 純正の AWT renderer 使っているので見た目が懐かしい感じですが、それはそれ。

ファイルに出力したい時はこうします:
new g5.SlideBuilder()({s{b("Look Ma, one line!")}})('application/pdf','one-line.pdf')

こんな仕上がり:

もう少しまともな例として G5 の紹介用スライドを G5 で書きました(途中まで)。こちらはまずは出来上がりから:


このスライドは次のコードで動いています:
import g5.SlideBuilder

def slideBuilder = new SlideBuilder()

def name = { 
    def attrs = [color:'#3283A1']
    slideBuilder.inline(it ? attrs + it : attrs, "G5") 
}
def title = { slideBuilder.block 'font-size':'48pt', 'margin-bottom':'18pt', it }
def properNoun = { slideBuilder.inline 'font-style':'italic', "\"$it\"" }

def slides = slideBuilder 'font-family':'Verdana', {
    slide {
        title { inline "Introduction to "; name('font-size':'64pt') }
        block "Nagai Masato" 
        block 'font-size':'16pt', color:'gray', {
            block "Twitter: @nagai_masato"
            block "Blog: http://nagaimasato.blogspot.com/"
        }
    }
    slide {
        title { inline "What is "; name(); inline "?" }
        list 'font-size':'24pt', {
            listItem { 
                image 'http://groovy.codehaus.org/images/groovy-logo-medium.png' 
                inline "Slideshow System" 
            }
            listItem {
                inline "Allows you to create and show slides using "; properNoun "Groovy"
            }
            listItem {
                inline "Uses "; properNoun "XSL-FO"; inline " and "
                properNoun "Apache FOP"; inline " for formatting slides"
            }
        }
    }    
} 
slides.show()

別名を使えば短く書けます。お好みでどうぞ。
import g5.SlideBuilder
 
def sb = new SlideBuilder()

def nm = { def attrs = [color:'#3283A1']; sb.i(it ? attrs + it : attrs, "G5") }
def tt = { sb.b 'font-size':'48pt', 'margin-bottom':'18pt', it }
def pn = { sb.i 'font-style':'italic', "\"$it\"" }

def ss = sb 'font-family':'Verdana', {
    s {
        tt { i "Introduction to "; nm('font-size':'64pt') }
        b "Nagai Masato" 
        b 'font-size':'16pt', color:'gray', {
            b "Twitter: @nagai_masato"
            b "Blog: http://nagaimasato.blogspot.com/"
        }
    }
    s {
        tt { i "What is "; nm(); i "?" }
        ls 'font-size':'24pt', {
            li { 
                im 'http://groovy.codehaus.org/images/groovy-logo-medium.png' 
                i "Slideshow System" 
            }
            li { i "Allows you to create and show slides using "; pn "Groovy" }
            li { 
                i "Uses "; pn "XSL-FO"; i " and "; pn "Apache FOP"
                i " for formatting slides" 
            }
        }
    }    
} 
ss()

G5 は こちら で公開しています。まだ開発ブランチにソースがあるだけですけど。Groovy 使いのみなさん、リリースされたら勉強会とかで使ってください ;)

時間がないので今日はこのへんで。