読者です 読者をやめる 読者になる 読者になる

GroovyServとGradle Daemonとの違い

10/23 9:50 訂正&追記: GradleがデフォルトでDaemon起動すると書いてた件 (thanks to きょんくん)

元記事: http://dev.classmethod.jp/server-side/gradle/gradle-first-contact/

経緯的な

id:daisuke-mが昼過ぎに技術を語るいつものシリーズにGradleを追加した旨のツイートをしてました。 生粋のGradlerとしては喜び勇んでチェックしてみたんですが、「Gradleのインストール」であるべきところを「Groovyのインストール」と書いてたので、親切心でコメントしたわけです。

そこまでは良かったのですが、ちょっと調子に乗ってしまいまして、

と書いたところ、記事に晒し上げされてしまいました。 なんということでしょう。

f:id:nobeans:20131022224044p:plain

そのままフェードアウトしてくれれば終わればまだ良かったのですが、自称怖くないモンスターの人に見付かって問い詰められているid:daisuke-mが忍びなさMAXでつい反応してしまったら、気がついたら私がGroovyServとGradle Daemonの違いについてなんか書かなければならない流れになっておりました。

ああ、恐ろしきかな因果の流れ。

GroovyServとは

Groovyコマンドを実行可能なJVMプロセスをサーバとして常駐させておいて、ネイティブコマンドを使ってTCP経由でコマンド引数をサーバに送り込み、評価した結果を再びTCP経由でもらって出力する、という拙作のツールです。

Groovyに限らずJVM系言語でスクリプトを書いた場合、JVMプロセスを起動して色々なクラスローディングする準備処理が遅くて、1秒ぐらいの待ち時間が発生します。

$ time groovy -e "println 'Hello'"
Hello

real    0m1.347s
user    0m1.481s
sys     0m0.168s

GroovyServをインストールして、クライアントコマンド経由で実行すると

$ time groovyclient -e "println 'Hello'"
Hello

real    0m0.057s
user    0m0.002s
sys     0m0.005s

すごく...はやいです...。

といっても、初回はサーバプロセスを起動するための待ち時間が数秒かかるんですけども。 一度起動したらそれ以降は上のような感じです。 はいはい、爆速爆速。

なお、mainクラスのエントリポイントを指定できればいいので、JythonとかClojureも同じように高速化できますScalaのコンパイルも頑張れば早くなるようですVim+QuickRunでgroovyConsole風にさくっと実行できるのにフル機能エディタ!みたいなこともできますし、SublimeText2からも似たようなことができます

一応このネタで2013/5にGr8conf EU 2013というデンマーク コペンハーゲンのGroovy系カンファレンスで発表してきたりしました。

詳しくは、ちょっと雑多な感じですがユーザガイド(日本語)をどうぞ。

Gradle Daemonとは

Gradleも同様にJVM上で動作するツールなので、起動の待ち時間が大変遅いわけです。 毎回JVMプロセスを一から起動してると、さくさくビルドやテストを試せません。 GroovyServみたいな仕組みが欲しいですよね。

ということで、Gradle本体が同じような仕組みを用意したのがGradle Daemonです。 歴史的に言うと、GroovyServの方が若干早いはず。 パクられた!とは言いませんが、時期的なものを考えると多少は参考にされたんじゃないかと自意識過剰気味にアピールしておきます。どうかな。違うかも。

最近のGradleはデフォルトでDaemonを利用します。 GradleがデフォルトでDaemon起動すると書いたな、アレは噓だ。 --daemonオプションが必要です。 以下はしれっと書き換えてあります。 すいません。

--daemonオプションはまだデーモンがいなければ起動して使い、すでに居ればそれを使うというオプションです。 というわけで、適当なGradleプロジェクトで、2回gradle --daemon tasksを実行してみると、2回目が劇的に早くなってるんじゃないかと思います。

たとえば、自宅のかなり古いMacBook mid 2008で試してみると、

$ gradle --daemon tasks
...
Total time: 12.02 secs

$ gradle --daemon tasks
...
Total time: 2.52 secs

とこんな感じです。すごいですね。 jpsしてみると

$ jps
9376 Jps
4136 GradleDaemon

とデーモンプロセスが常駐してるのが分かるはず。

あと--daemonオプションが付けられていようが、--no-daemonというオプションを追加すれば、デーモンは無視して自前プロセスで実行できたりします。 デーモンプロセスありきでalias設定してあっても、一時的にはずせるわけですね。

また、デーモンプロセスが要らなくなったら、普通にkillコマンドでもいいですが--stopオプションを使えばgradleコマンド経由でコントロールできます。

個人的にはGradle Wrapperを優先しつつDaemonは必須で使いたいので、以下のようなaliasをシェルで設定して使っています。

gradle='if [ -x ./gradlew ]; then GRADLE_BIN=./gradlew; else GRADLE_BIN=\gradle; fi; $GRADLE_BIN --daemon'

こちらも詳しくはユーザガイド(日本語)をどうぞ。

ちなみにGroovyServもビルドにGradleを使っていて、Daemon様におかれましては大変お世話になっております。

GroovyServとGradle Daemonの違い

Gradle Daemonは、Gradleタスクの起動処理をショートカットする
GroovyServは、Groovyスクリプトの起動処理をショートカットする
Gradle Daemonは、gradleコマンドのオプションですべて操作する
GroovyServは、groovyclientコマンドのオプションで操作するほかに、サーバプロセス起動用のgroovyserverコマンドがある
Gradle Daemonは、ユーザの気付かないうちにデーモンプロセスが常駐してる
GroovyServは、サーバプロセスの起動時にコンソール出力してアピールする

気の利いた大喜利風にオチがつけばいいのですが、実力的に無理でした。ごめんなさい。

結局、コンセプト的なものは一緒ですけど、用途が違うので細かいところで色々と違いますね。 あんまりまじめに比べるモノじゃないと思われます。 むしろGroovyServと比べるべきはNailgunとかDripあたり。

おまけ: NailgunとGroovyServの違い

Nailgunはよく知られたJVMプロセス常駐系アーキテクチャのツールです。 歴史的にはGroovyServよりも古いです。 古すぎて2005年以降はまったく更新されていません。 と思ったら、2012/11月にv0.9.1がリリースされててちょっと驚きました。

NailgunはピュアJava実装なのでビルトイン目的によく使われます。 というかAPI的にもそれっぽい感じ。 JRubyもNailgunを使ってますね。

GroovyServはGroovyで実装してるのでビルトイン目的には向いてません。 GroovyServは今更ピュアJava路線に変更するのは不可能ではないにしろ面倒つらい感じです。 ビルトインの代わりに、単にクライアントの引数でmainクラスのエントリポイントを指定するだけで色々なJVM言語の起動時間短縮を計れます。物は言いようです。

機能的な違いでいえば、下のような感じです。

  • GroovyServのみサポート
    • アクセス元アドレスと認証トークンを使ったアクセスコントロール
      • 許可されたアドレス以外のマシンからのコマンド実行はエラー
      • 同一マシンでも認証トークンが一致しないとコマンド実行はエラー
    • クライアント側のCLASSPATH環境変数をサーバ側に一時的に反映する
    • クライアント側の任意の環境変数をサーバ側に永続的に反映する(一時的反映がベストであるが現状は永続的)
    • クライアント側のカレントディレクトリをサーバ側に一時的に反映する
    • クライアント側のCtrl-Cでサーバ側の処理を中断する
  • 両方ともサポート
    • System#exit()が実行されてもJVMは落ちない

ほらこうやってみるとGroovyServ結構がんばってるでしょ? Nailgun側のメリットはビルトイン以外に思いつきませんが、他に何かあるかもしれません。 フェアじゃないのは単に知識不足です。すいません。

おまけ: DripとGroovyServの違い

Dripはシンプルで割り切ったアーキテクチャで中々興味深いところはありますが、アーキテクチャやサポート機能が違いすぎてGroovyServとはあまり競合しないふいんき。

簡単に言うと、

http://b.hatena.ne.jp/nobeans/20130904#bookmark-109556574

GroovyServのようなJVMプロセス常駐系/興味深いアプローチ/単一プロセスを使い回すとdirtyになっていくのでJVM引数,クラスパス,起動クラスのハッシュごとに異なるJVMデーモンを使う/コードベースが超小さい/bash限定は楽そう

という感じです。

Dripは、bashを前提にしているので色々実装がシンプルです。うらやましいです。

GroovyServは、Mac/Linux/Windowsで動作します。
現状ではWindowsサポートが若干手薄な気もしますが、次期バージョンではできるだけ環境ごとの機能差がないように頑張っています。
Dripは、カレントディレクトリ毎に別プロセスを起動します。
よって、複数プロセスが常駐します(一定時間でタイムアウトして消える)。
既存JVMプロセスに対するネイティブレベルでのCWD制御とかを不要にしたりしてます。割り切り方が素敵です。

GroovyServは、ポート毎に1つのサーバプロセスを用意しています。
普通はデフォルトポートで十分なので、サーバプロセスは1つだと思えばOKです。ある意味シンプル。
CWDなどは毎回力業で反映しています。

こんな感じでしょうか。

測定マシンについて一言

なお、上記記事中の性能的なものはすべて自宅の超古いMacbook 2008 Midで測定してます。 そうとう遅いです。 苦痛です。 今晩のAppleのイベントで新MBPでたら速攻で買い換えたい所存です。

まとめ

というわけで、GVMを使ってGradleをインストールしたら、ついでに

$ gvm install groovy
$ gvm install groovyserv

すると良いと思います。 更に手が滑って

$ gvm install gaiden

Gaidenのインストールまで進んだあなたはファッションリーダとしての自覚と風格が感じられて頼もしい限りです。