Java8のInvokeDynamic実装一新によるGroovyのindyモードの性能改善効果が凄すぎた件
本日、Java Day Tokyo 2014に来ています。
で、ついさっきのセッションで「JDK8ではInvokeDynamic(以下、indy)の実装を一新したのですごく速くなったよ」という話を聞いたので、Groovyのindyモードで試してみました。
Groovyは2.0(現在は2.3)でindy対応されています。
それ以前はGroovy独自機構による性能改善が行われていました。
しかし、JDK7からのホヤホヤなindyを前提にしてしまうと、JDK6で動かなくなってしまいます。
Groovyは下位互換性が重視されているので、これは問題です。
というわけで、indyが使えないJDK上でも使えるように既存の独自機構もそのまま残っているし、indy不要バージョンが標準になっていて、indyオプションを利用可能にするには$GROOVY_HOME/lib
配下を$GROOVY_HOME/indy
配下にあるファイルで上書き置換してあげないといけません。
と言うぐらいに色々面倒くさいのですが、じゃあそこまでやってindyを使ったところでどれだけ嬉しいのかというと、JDK7までは「全然うれしくない」が答えでした。 JDK7のindy実装がこなれてない&Groovyのindy対応もカリカリではないので、全然性能がでなかったんですね。 独自機構の方が全然速かったわけです。
さて、「JDK8ではInvokeDynamic(以下、indy)の実装を一新したのですごく速くなったよ」という話を受けて試してみたんですが、indyモード&JDK8すごくヤバいことになってました。
サンプルコード
コードとしてはフィボナッチ数の算出です。 ローカルにあったファイルを発掘したもなので、実装が云々というツッコミはこの際受け付けません。
class Fibonacci { def calc = { n -> return (n == 0) ? 0 : (n == 1) ? 1 : call(n - 1) + call(n - 2) } static void main(String[] args) { def num = args[0] as int println "Fib($num) = ${new Fibonacci().calc(num)}" } }
これをJDKの7u55と8u05で実行してtimeコマンドで計ってみました。 厳密なベンチではないので参考ぐらいのやさしい気持ちで読んでください。 ちなみにGroovyは最新ホヤホヤの2.3.0です。
Groovy 2.3.0 (indy有効) + JDK 7u55
$ java -version java version "1.7.0_55" Java(TM) SE Runtime Environment (build 1.7.0_55-b13) Java HotSpot(TM) 64-Bit Server VM (build 24.55-b03, mixed mode) $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 42.03s user 0.35s system 102% cpu 41.538 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 33.75s user 0.34s system 102% cpu 33.197 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 41.01s user 0.35s system 102% cpu 40.512 total
Groovy 2.3.0 (indy有効) + JDK 8u05
$ java -version java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 20.04s user 0.29s system 108% cpu 18.777 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 19.73s user 0.29s system 108% cpu 18.520 total $ time groovy --indy Fibonacci.groovy 40 Fib(40) = 102334155 groovy --indy Fibonacci.groovy 40 20.27s user 0.31s system 107% cpu 19.076 total
実に2倍。これはヤバい。
(参考)Groovy 2.3.0 (indy無効=Groovy独自機構) + JDK 8u05
参考までにindyを使わないGroovy独自機構による性能も見てみましょう。
$ java -version java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 25.60s user 0.30s system 106% cpu 24.407 total $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 23.40s user 0.28s system 107% cpu 22.031 total $ time groovy Fibonacci.groovy 40 Fib(40) = 102334155 groovy Fibonacci.groovy 40 23.68s user 0.28s system 107% cpu 22.288 total
なんと、JDK7+indyにはダブルスコアで勝ってたのに、JDK8+indyに追い越されてしまいました。
indy全盛時代の幕開けを感じますね。
というようなことを、Java-ja枠のTDDセッションを聞きながらまとめてみました。