BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル GroovyServ - 高速起動Groovy -

GroovyServ - 高速起動Groovy -

本記事ではGroovyServを紹介します。GroovyServの基本的なアイデアの説明に始まり、実際の効果を示した上で、導入方法と簡単な使い方、応用例などについても説明します。

GroovyServとは何か

GroovyServは、筆者が所属しているNTTソフトウェア株式会社において、Apache License, Version 2.0に基づき開発・公開しているオープンソースソフトウェアです。現在、Mac OS X、Linux、Windows版で動作確認を行い、これらの環境用のバイナリ版も公開しています。2010年3月9日に0.1版が公開され、2010年8月10日時点では0.4版が公開されています。

GroovyServの目的は、Groovyで書かれたプログラムの起動時間を短縮することです。環境によりますが、Groovyプログラムの起動には1秒~数秒かかることは珍しくありません。GroovyServを用いることで、条件にもよりますが、起動に要する時間を最大数十分の一程度に短縮することができます。

起動時間短縮の原理は、Groovyの処理系を、あらかじめサーバ(groovyserver)として常駐起動させておき、Groovyスクリプト実行はgroovyserverに処理を移譲する小さなCプログラム(groovyclient)を通じて行うというものです。JVMの起動や、基本的なクラス群のロード、初期化を、サーバ起動時にあらかじめ行っておくことで、個々のgroovyclientの実行ではそれが不要となり高速化します。groovyserverとgroovyclient間はTCP/IP通信で行います。

起動速度がなぜ問題か

Groovyでスクリプトを書いたり、あるいはワンライナーで利用する場合に起動速度は特に重要です。主観によりますが、筆者にとっては、スクリプトの実行開始までに1秒「も」待たされるのは、非常に長く感じられます。特に、PerlやRubyなどのスクリプト言語に慣れていると「耐えられない」レベルかもしれません。

スクリプトの開発は、出力結果の様子を見ながら、試行錯誤的に記述を修正していくため、起動速度が重要になります。1秒はとても待っていられません。

Groovyはその機能からして、本来PerlやRuby、Pythonなどにも拮抗しうる強力なスクリプト言語ですが、GroovyServを併せて使うことで、スクリプト言語としてのGroovyの本来のパワーを最大限に引き出すことができるようになります。

起動性能ベンチマーク

GroovyServを用いた場合の起動時間、具体的には以下のコマンドラインの実行に要する時間を計測してみます。

$ time groovy -e "println 'hello'"

この測定方法だと、起動時間だけではなく処理時間や終了に要する時間も含みますが、それは十分に小さいと仮定しています。

Mac OS Xでの起動速度の測定結果を[グラフ1]に示します(グラフ縦軸目盛りの単位はnormal groovyとの比率。秒数はMinibook参照)。通常版のGroovyでは、起動時間が1秒~3秒以上かかっていたのが、GroovyServによって0.03秒(MacOS X)にまでに短縮することができています(CPU性能や搭載メモリ量などの条件によっては、この差は増減します)。

[グラフ1] 起動時間ベンチマーク(MacOS X)

 

 

normal groovyとの比率

測定条件など

  • 起動時間ベンチマーク: MacOSX 10.6.3, Groovy 1.7.3, GroovyServ 0.4, MacBook Core2duo 2.53GHz, JDK1.6.0u20
  • timeコマンドでreal時間を計測。十回実行した平均。

処理性能ベンチマーク

次に、起動時間ではなく処理時間も計測してみます。測定対象のプログラムは以下の通り。

start = System.currentTimeMillis();
for (i=0; i<1000000; i++) {};
println System.currentTimeMillis() – start

[グラフ2]に結果を示します。

これでわかるのは、まず、上のコードに関しては、Client VMではなくServer VMを使うことで2倍程度の性能向上が見られるということです。この効果は、GroovyServを使っても使わなくても得られます。

また、Server VM同士、Client VM同士で比較したときに、GroovyServを使った場合に処理時間が向上しているのも興味深いところです。つまりGroovyServを適用すると、起動が早くなるだけではなく、HotSpot(Server VM)の影響とも独立に、有意に処理速度が速くなるという現象が見られます。この理由は検証できていませんが、おそらくJVMの起動/停止が無くなることでキャッシュヒット率が上がったり、GCの影響があったりするのかもしれません。

それはともかく、Server VMとClient VMには実行速度と起動時間のトレードオフがありますが、GoovyServを使うことで、毎回の起動時間増大というデメリットを負うことなしに、Server VMで得られる実行速度向上のメリットを享受できるようになります。

なお、今のところGroovyServ自体にはServer VM/Client VMを指定する機能はありませんが、Groovyの機能として、JAVA_OPTS環境変数でJVMの起動オプション-serverもしくは-clientを指定することができます。

[グラフ2] 実行時間ベンチマーク(Windows)

実行時間(ミリ秒)

測定条件など

  • Groovy 1.7.3, GroovyServ 0.4, Windows XP SP3(cygwin 1.7.5), Core2duo 2.00GHz, JDK1.6.0u13
  • timeコマンドでreal時間を計測。十回実行した平均。

インストール

GroovyServのインストール手順について記述します。まず前提として、以下を確認してください。

  1. JDKがインストールされていること。JDK 1.6以降で動作確認しています。
  2. Groovyがインストールされていること。1.7.3以降が推奨です。

実際のインストールですが、Windows環境の場合、Groovy 1.7.4以降の標準配布パッケージのWindows Installer版にGroovyServが同梱されるので、それをインストールするのが手っ取り早いでしょう [1] 。

脚注

[1] ただ、残念ながら、本記事筆時点で最新版のGroovy 1.7.4版の Windows Installer版に含まれているGroovyServ 0.3は問題が多くお勧めできません。次版以降であれば比較的問題なく利用可能なはずですのでそちらをお使いください。

それ以外の環境の場合は以下のようにします。

  1. GroovyServのホームページ から、対応するOS用のバイナリパッケージをダウンロードして任意のフォルダに展開します。このフォルダを<GROOVYSERV_HOME>とします。
  2. 環境変数PATHに<GROOVYSERV_HOME>/binを追加します。
  3. 環境変数GROOVY_HOMEが正しく設定されていることを確認します。

上記がすんだら、コマンドプロンプトから以下を実行してみてください。

$ groovyclient -v
starting server...
.Groovy Version: 1.7.3 JVM: 1.6.0_12

$ groovyclient -v
Groovy Version: 1.7.3 JVM: 1.6.0_12

のように表示されればインストールに成功しています。二回目の起動は高速化されているはずです。

使い方

GroovyServには二つのコマンドが含まれています。groovyclientとgroovyserverです。

groovyclient

groovyclientはもっともよく使うコマンドであり、groovyコマンドの代替として利用します。以下のようなaliasを定義すると便利でしょう。

alias groovy=groovyclient (bashの場合)
doskey groovy=groovyclient (Windowsのコマンドプロンプトの場合)

groovyclientに与えられたコマンドライン引数やオプションは、基本的にそのままgroovyserverに転送されます。groovyclientを実行した時点で、groovyserverが起動していなければ、groovyserverが透過的に起動されます。初回は数秒~十数秒の起動待ち時間が生じますが、次回以降は高速な実行となります。

groovyclientは、groovyコマンド互換であり、-eはもとより、-p、-n、-i.bakなどのワンライナー向けのオプションの指定も可能です。以下は指定したファイルを拡張子.bakでバックアップファイルとしてコピーした上で、内容をすべて大文字に変換したもので元のファイルを置き換えます。

$ groovyclient -i.bak -pe 'line.toUpperCase()' input.txt

groovyserver

groovyserverはGroovyServのサーバ側を制御するためのコマンドです。以下のオプションがあります。

  • -k 停止
  • -r 再起動
  • -v 詳細ログ出力

ログ情報は、~/.groovy/groovyserv/groovyserver.logに出力されます。

なお、現在のGroovyServでは、Windows環境下では-k、-rオプションは利用できません [2] 。その代わり、groovyserverプロセスがウィンドウとして最小化されて起動されるので、ウィンドウを閉じることでサーバを終了することができます。

脚注

[2] 厳密に言うと、cygwin環境でシェルスクリプト版groovyserverを明示的に使って起動した場合には、-r、-kオプションを使用することができます。

動作の仕組み

GroovyServの構成図を[図1]に、GrovyServを使わずに、通常のGroovyでスクリプトを実行する様子を[図2]に示します。

[図1] GroovyServを用いたスクリプトの実行

[図2] 通常のGroovyにおけるスクリプトの実行

このように、groovyclientは、groovyコマンドおよびそれを実行するJVMの代替として、本来それらが受け取っていた入力情報を代わりに受け取り、TCP/IPの独自プロトコルに変換し、groovyserverに転送します。入力情報には以下が含まれています。

  • コマンドライン引数、オプション
  • CLASSPATH環境変数の値
  • SIGINTシグナル(Ctrl-C入力時など)
  • 標準入力ストリーム

また、groovyserver上で実行されるUser Groovy Scriptからの出力は、groovyserverが受け取り、groovyclientに対してTCP/IPで転送されます。groovyclientは、受け取ったそれらがあたかも自プロセスからの出力であるかのように振る舞います。例えば、GroovyスクリプトでSystem.exit()を呼び出すと、groovyserverが終了するのではなく、groovyclientがそのステータスで終了します。

出力情報には以下が含まれています。

  • exitステータス
  • 標準出力/標準エラー出力ストリーム

分散実行の禁止とセキュリティ

GroovyServのクライアントとサーバは同一マシン上で動作させることが前提です。このことには、大きく二つの理由があります。

一つは、スクリプトからファイル操作を行う場合、スクリプトから見えるファイルシステムと、groovyserverを起動する環境から見たファイルシステムが同一であることが必要だからです。

二つ目の理由はセキュリティ上の問題からです。groovyserverでは任意のスクリプトが実行できるので、ネットワークを通じて別のマシンからスクリプトが実行できてしまうと、深刻なセキュリティ上の問題になる可能性があります。このため、GroovyServではリモートマシンからの接続を禁止しています。また、接続時に「認証クッキー」([図1]の「Authentication Cookie File」)を使用して、groovyserverを起動した同じマシン、同じユーザーからの接続でないと処理を受け付けないようになっています。

パイプと多重実行

GroovyServの主目的は、コマンドラインからスクリプトを実行することです。従って、複数のスクリプトの実行を、パイプを使ってつなぐこともできます。

$ groovyclient -e "new File('.').eachFile{println it}" | groovyclient -pe 'line.toUpperCase()'
./.ABBREV_DEFS
./.ANDROID
./.ANT
./.BASH.D
./.BASH_HISTORY
./.BASH_PROFILE
./.CLOJURE
./.CLOJURE.CONF
:

この場合、各groovyclientプロセスに対応する処理は、groovyserver内の複数のJavaスレッドとして並行実行されます。

GroovyServの応用とTIPS

GroovyServの応用例やTIPSをいくつか示します

シェルからの利用

以下に、シェルからGroovyServを使う際に設定しておくと便利かもしれないaliasや関数を示します(bashの場合)。

println() { eval "groovyclient -e 'println($*) ' "; }
alias groovyc="groovyclient -e
'org.codehaus.groovy.tools.FileSystemCompiler.main(args)'"
alias groovyConsole="groovyclient -e 'groovy.ui.Console.main(args)'"
alias grape="groovyclient –e
 'org.codehaus.groovy.tools.GrapeMain.main(args)'"

特にprintlnはコマンドラインからちょっとした計算を行うのに便利です。

$ println 3+5*2
30

$ println
'System.getProperties().keys().findAll{it.startsWith("groovy")}'
[groovy.source.encoding, groovy.runningmode, groovy.starter.conf,
groovy.classpath, groovy.home]

テストでの利用

Groovyにはテストを書くのに便利な機能やライブラリがあり、テストコードを書くために利用するケースも少なくないでしょう。テストコードの実行は頻繁に行われるため、起動時間の短縮は重要です。テストをGroovyで記述するなら、GroovyServを適用することを検討してみてはいかがでしょう。リズムを保ち、てきぱきとした開発を行うことができます。

統合開発環境(IDE)からの利用

Eclipseなどの統合開発環境では、外部ツールとしてgroovyclientを定義しておくと便利です。たとえば、Eclipseで[Run]-[Extenal Tool]-[External Tools Configration]で、下図のようにgroovyclientの起動を設定しておくと、「現在開いているGroovyコードを実行する」ことができます。この設定を行った上で、ショートカットキーにバインドしておくと良いでしょう。テストコードをIDEから実行するときを含め、便利に利用できます。

Javaクラスの呼び出し…scalacを例に

GroovyServでは、Groovyスクリプトを実行するだけではなく、Javaのクラス/メソッドを直接指定して呼び出すことができます。つまり、JVM上で動作する任意のコードをGroovyServを通じて起動することができます。以下はnative2asciiコマンドをGroovyServで起動する例です。

$ groovyclient -e 'sun.tools.native2ascii.Main.main(args)'

GroovyServはGroovyスクリプトの起動を高速化するものですが、Groovyから呼び出せるものはGroovyで書かれている必要はありません。つまりJVM上で動くものであれば、GroovyServによる起動の高速化が理論的には可能です。たとえば、Scalaの処理系やScalaで書かれたプログラムもその例外ではありません。起動クラス名がわかれば、以下のように実行することで、scalac(Scalaコンパイラ)を実行できます。

$ time groovyclient -Dscala.home=$SCALA_HOME –e
 'scala.tools.nsc.Main.main(args);' -- hello.scala

効果をみてみましょう。コンパイル対象は以下のようなScalaクラス定義です。

object HelloWorld {
 def main(args: Array[String]) {
    println("Hello GroovyServ!")
 }
}
 

実行結果を[グラフ3]に示します。scalac、fsc [3] はそれぞれオリジナルのScala付属のものであり、scalac/GroovyServとfsc/GroovyServがそれらをGroovyServ上で実行した結果です。

[グラフ3] scalacの起動時間高速化ベンチマーク

scalacとの比率

脚注

[3] fsc(Fast Scala Compiler)は、Scalaに付属するコンパイル高速化の仕組みであり、 GroovyServと同様にプロセスとして常駐することでコンパイル時間を短縮させます。 fscのクライアントは、JVM上の(Scalaで書かれた)プログラムであり、 Scalaのコンパイルタスク専用のサーバ(コンパイレーションサーバ)にコンパイル処理を移譲するものです。

上記の結果から見る限りでは、scalacをGroovyServ上で動作させた場合と、fscを比較すると、scalac/GroovyServの速度がやや速いですが、ほぼ同等といえます。GroovyServは汎用のプログラムであることを考えると、悪くはありません。

しかし、特筆すべきことは、fscをGroovyServ上で実行することで、さらに顕著な起動速度の向上(16.5倍)をもたらすことができるということです。これは、fscによる、scalacの処理内容を考慮した起動時間の短縮化に加えて、JVM自体の起動時間をGroovyServが削減し、効果が累算されたためだと考えられます。この場合、二つのJVMプロセスが常駐することになります。

今回は、通常のmain()メソッドを呼ぶことでscalacやfscを起動しましたが、 Groovyで柔軟なカスタム起動コードを書くこともできます。このことはGroovyServを一般的なJavaアプリケーションの常駐実行の仕組みと見たときの、利点の一つでもあります。

まとめ

GroovyServは、Groovyのスクリプト言語としての有用性をさらに高めるものです。また、それだけにとどまらず、Java技術ファミリーの有用性を高めるものにしたいと願っています。Groovyの経験をお持ちの方も、そうでない方も、本記事を読まれて興味をもたれた方は、是非使ってみてください。皆さんからのフィードバックをお待ちしております。

参考リンク

http://kobo.github.com/groovyserv/

http://groups.google.com/group/groovyserv/

http://www.slideshare.net/uehaj/groovyserv-concept-how-to-use-and-outline

http://www.slideshare.net/nobeans/groovyserv-technical-part

http://groovy.codehaus.org/

著者

•コミュニティ名: JGGUG(日本 Grails/Groovy ユーザーグループ)

•会社名: NTTソフトウェア

•個人名: 上原潤二/中野靖治

•Mail: uehaj@jggug.org, ynak@jggug.org

こちらの記事のプリントアウトをご希望の方は、
より詳細な情報を掲載したMiniBookを掲載しておりますので、
そちらからダウンロードください
 http://www.infoq.com/jp/minibooks/groovyserv

この記事に星をつける

おすすめ度
スタイル

BT