Groovy 2.0でindyを使う
Java 7では、Java VM上での動的言語の実行を効率化することを目的とした一連の機能拡張(JSR 292: Supporting Dynamically Typed Languages on the Java Platform,日本語解説)がなされました。具体的にはたとえば、Java Bytecode命令の1つにinvokedynamicという命令が追加されました。このinvokedynamicの命令名からとって、これらの機能拡張のことを「indy」と略することが多いようなので、本記事でもそう呼びます。
indyがターゲットとするところの「Java VM上での動的言語」の1つであるGroovyでも、バージョン2(Groovy 2.0)からindy対応になっとったわけです。しかし、Groovy 2.0のindy対応は、デフォルトでは有効ではなく、有効にするにはちょっとした設定が必要です*1。
本エントリではその方法について解説します。
準備
Groovyでindyを有効化するには以下が必要となります。
indyを有効にするための設定
やるべきことは2つあります。
indyのためにその1: --indyオプション
groovyコマンド、もしくはgroovycコマンドでgroovyプログラムを実行する場合、indyを有効化するためには、これらのコマンドの実行において「--indy」オプションを指定することが必要です。
なお--indyオプションを指定すると、内部的には、内部的に実行されるGroovyコンパイラ設定を表すorg.codehaus.groovy.control.CompilerConfigurationオブジェクトに対して
.getOptimizationOptions().put("indy", true);
が設定されます。GroovyShellなどを使ってGroovyコードを実行する場合も、同様にCompilerConfigurationを設定すれば良いわけです。
indyのためにその2: indy対応のライブラリを使用する
さて、--indyオプションだけでは以下のようなエラーになります。
$ groovy -indy -e "println 'hello' " org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: General error during class generation: Cannot use invokedynamic, indy module was excluded from this build. groovy.lang.GroovyRuntimeException: Cannot use invokedynamic, indy module was excluded from this build.
こうなる理由は、groovy-2.0.x.jarがindy対応のものではないからです。
対処としては、以下の2つの方法があります。
- indy対応ライブラリを使用する方法1:groovy-all-2.0.x.jar, groov-2.0.x.jarを置き変える
この記事によれば、以下の上書きコピーを行います(元に戻せるようにコピー先はバックアップしといた方が良いでしょう)。
コピー元 | コピー先 |
自分のアプリケーションなどにgroovy-all-x.x.x.jarを組み込んで使っていた場合などは、
寄り道して、これらの-indy.jarで、本当にinvokedynamic命令が使用されているかを確認してみます。
$ javap -c -classpath /tool/groovy-2.0.1/target/install/indy/groovy-ant-2.0.1-indy.jar groovy.util.FileNameFinder | grep dyn 30: invokedynamic #56, 0 // InvokeDynamic #0:getFileNames:(Lgroovy/util/FileNameFinder;Ljava/util/Map;)Ljava/lang/Object; 41: invokedynamic #56, 0 // InvokeDynamic #0:getFileNames:(Lgroovy/util/FileNameFinder;Ljava/util/Map;)Ljava/lang/Object; 42: invokedynamic #99, 0 // InvokeDynamic #1:fileScanner:(Ljava/lang/Object;Lgroovy/lang/Closure;)Ljava/lang/Object; 69: invokedynamic #106, 0 // InvokeDynamic #1:iterator:(Ljava/lang/Object;)Ljava/lang/Object; 107: invokedynamic #119, 0 // InvokeDynamic #1:getAbsolutePath:(Ljava/lang/Object;)Ljava/lang/Object; 112: invokedynamic #123, 0 // InvokeDynamic #1:leftShift:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
おお。確かに。
- indy対応ライブラリを使用する方法2: groovy-starter.confを書き変える
これは適当に自分で試して見付けた方法なので、保証なしです。でも、上のようにファイル名を変えて上書きコピーとかするよりもマシな気がしています。
書き変える内容は、「load !{groovy.home}/indy/*.jar」を追加するということです。
% diff -c target/install/conf/groovy-starter.conf~ target/install/conf/groovy-starter.conf *** target/install/conf/groovy-starter.conf.old 2012-08-13 16:50:29.000000000 +0900 --- target/install/conf/groovy-starter.conf 2012-08-13 16:56:29.000000000 +0900 *************** *** 15,20 **** --- 15,21 ---- # load required libraries load !{groovy.home}/lib/*.jar + load !{groovy.home}/indy/*.jar # load user specific libraries load !{user.home}/.groovy/lib/*.jar
この状態で
$ groovy -indy -e 'println "hoge" ' hoge
ぱちぱち。
効果のほどは?
今、ベンチマークを試してますが、jdk 7u5に関して、多少速くなる時もあり、むしろ遅くなるときもあり。CompileStaticほどの向上効果は得られていないですね。--indyをつけると既存コードが実行できなくなるバグらしきものも1個だけ発見しました。まとめて別途報告します。もちろんこれは今の最適化しきれてないとも言われていjvmの、かつ私が試した範囲のはなしなので一般化しないでくださいますよう。
番外編
Groovyをソースコードからコンパイルをする
groovy 2.0からは、build.xmlが除去されてます。なので、gradleでコンパイルする必要があります。といっても何も心配する必要はありません。みんな大好きgradlew(Gradle wrapper)が用意されているからです。
以前、ant時代にgroovyのコンパイルに使用していた「ant install」に相当する処理(
./gradlew installGroovy
ただのinstallタスクは、mavenのリポジトリへのインストールになるようです。
Gradleのビルドスクリプト中でgroovycに--indyオプションを与える
ちなみに、Gradleのビルドスクリプト中からgroovycを呼び出してコンパイルする際にgroovyに--indyオプションを与えるためには、
compileGroovy { groovyOptions.optimizationOptions.indy=true }
こういう指定をしておけば良いです。(このオプションoptimizationOptions.indyはGradle 1.1から利用可能)
groovyのMavenモジュール(pom)を使う。
groovy自体をMavenのリポジトリからダウンロードして使用する場合、indy版のjarはclassifier(バリエーションのようなもの)として登録してあります。例えば、gradleであれば、
dependencies { groovy group: 'org.codehaus.groovy', name: 'groovy-all', version:'2.0.1', classifier:'indy' }
のようにclassifier:'indy'を指定するとindy対応のgroovy-allが利用されるようになります。