JRubyいろいろ - 起動時オプション(コマンドラインオプション)

インストール編(http://d.hatena.ne.jp/yokolet/20100529#1275156722)に書いたどれか(もしかするとさらに他のインストール方法もあるかも。。。NetBeansに付いてきたとか)で、JRubyをインストールできたら、次は当然、使っちゃうぞー!でしょう。では、さっそく。


その1 動く?
定番、Hello Worldの前にやっておくのは、ほんとーにJRubyが動いている?を確認すること、ですね。Hello Worldが動かなかったときに、なにがまずいかを特定するためにも必要。ので、最初はコレ。

jrubyコマンドへのパスは通してあるよね!、じゃ、

jruby -v
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.5.0_24) [i386-java]

バージョン番号、でたかな?まれに、JDKのバージョンと相性が悪くて動かないことがあるみたいです。すでに一年前の話しなので、状況は変わっていると思いますが、
http://kenai.com/projects/jruby/pages/JRubyOnUbuntu
http://kenai.com/projects/jruby/pages/JRubyOnHPUX11_23
なんかがあるので、JRubyが動いているかどうかの確認をお忘れなく。


その2 定番、Hello Worldは?
定番、定番。もう、コレをやらなきゃプログラミングは始まらないよねー。JRubyの場合、基本的にRubyと同じなので、

jruby -e"puts 'Hello World'"
Hello World

とか、

jruby -e"puts \"Hello World\""
Hello World

とかして、-eオプションをつければ"(double quote)で囲まれた中を評価してくれる、と。日本語だって、

jruby -e"puts 'JRubyいいよぉ。使おうよ!'"
JRubyいいよぉ。使おうよ!

というように、ちゃんと表示してくれます。

もう一つ、Rubyならではのirbを使ってHello Worldしてみるというのも、もちろんあります。JRubyにはirbの他にjirbという最初に"j"をつけたコマンドもあるのですが、一番よく使われているのは "jruby -S irb" ではないかと。なぜかというと、jrubyを使っているということがわかりやすいし、いろいろなオプションを指定したいときはコレが一番だからです。

jruby -S irb
irb(main):001:0> puts 'JRubyいいよぉ。使おうよ!'
JRubyいいよぉ。使おうよ!
=> nil
irb(main):002:0> 


その3 オプションいろいろ
と、ここまでで、すでに3つのコマンドラインオプション-v, -e -Sが出てきました。。。他には?、、、よりどりみどり揃えてございます。。。なのがJRubyRubyとはちょっと違うところですね。-hオプションを叩いてみると、、、

jruby -h
Usage: jruby [switches] [--] [programfile] [arguments]
  -0[octal]       specify record separator (\0, if no argument)
  -a              autosplit mode with -n or -p (splits $_ into $F)
  -b              benchmark mode, times the script execution
  -c              check syntax only
  -Cdirectory     cd to directory, before executing your script
  -d              set debugging flags (set $DEBUG to true)
  -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
  -Fpattern       split() pattern for autosplit (-a)
  -i[extension]   edit ARGV files in place (make backup if extension supplied)
  -Idirectory     specify $LOAD_PATH directory (may be used more than once)
  -J[java option] pass an option on to the JVM (e.g. -J-Xmx512m)
                    use --properties to list JRuby properties
                    run 'java -help' for a list of other Java options
  -Kkcode         specifies code-set (e.g. -Ku for Unicode, -Ke for EUC and -Ks for SJIS)
  -l              enable line ending processing
  -n              assume 'while gets(); ... end' loop around your script
  -p              assume loop like -n but print line also like sed
  -rlibrary       require the library, before executing your script
  -s              enable some switch parsing for switches after script name
  -S              look for the script in bin or using PATH environment variable
  -T[level]       turn on tainting checks
  -v              print version number, then turn on verbose mode
  -w              turn warnings on for your script
  -W[level]       set warning level; 0=silence, 1=medium, 2=verbose (default)
  -x[directory]   strip off text before #!ruby line and perhaps cd to directory
  -X[option]      enable extended option (omit option to list)
  -y              enable parsing debug output
  --copyright     print the copyright
  --debug         sets the execution mode most suitable for debugger functionality
  --jdb           runs JRuby process under JDB
  --properties    List all configuration Java properties (pass -J-Dproperty=value)
  --sample        run with profiling using the JVM's sampling profiler
  --client        use the non-optimizing "client" JVM (improves startup; default)
  --server        use the optimizing "server" JVM (improves perf)
  --manage        enable remote JMX management and monitoring of the VM and JRuby
  --headless      do not launch a GUI window, no matter what
  --1.8           specify Ruby 1.8.x compatibility (default)
  --1.9           specify Ruby 1.9.x compatibility
  --bytecode      show the JVM bytecode produced by compiling specified code
  --version       print the version

いやぁ、けっこうあるね。Rubyな人にはお馴染みのオプションが半分くらいあるけれど、見たことがないオプションも結構あるかも。ところが、実はこれだけじゃあない。もっとあるんです。もうひとつのヘルプオプション --propertiesを付けてJRubyを起動してみると、、

jruby --properties
These properties can be used to alter runtime behavior for perf or compatibility.
Specify them by passing -J-D=

COMPILER SETTINGS:
    jruby.compile.mode=JIT|FORCE|OFF
       Set compilation mode. JIT is default; FORCE compiles all, OFF disables
    jruby.compile.fastest=true|false
       (EXPERIMENTAL) Turn on all experimental compiler optimizations
    jruby.compile.frameless=true|false
       (EXPERIMENTAL) Turn on frameless compilation where possible
    jruby.compile.positionless=true|false
       (EXPERIMENTAL) Turn on compilation that avoids updating Ruby position info. Default is false
    jruby.compile.threadless=true|false
       (EXPERIMENTAL) Turn on compilation without polling for "unsafe" thread events. Default is false
    jruby.compile.fastops=true|false
       (EXPERIMENTAL) Turn on fast operators for Fixnum. Default is false
    jruby.compile.fastcase=true|false
       (EXPERIMENTAL) Turn on fast case/when for all-Fixnum whens. Default is false
    jruby.compile.chainsize=
       Set the number of lines at which compiled bodies are "chained". Default is 500
    jruby.compile.lazyHandles=true|false
       Generate method bindings (handles) for compiled methods lazily. Default is false.
    jruby.compile.peephole=true|false
       Enable or disable peephole optimizations. Default is true (on).

JIT SETTINGS:
    jruby.jit.threshold=
       Set the JIT threshold to the specified method invocation count. Default is 50.
    jruby.jit.max=
       Set the max count of active methods eligible for JIT-compilation.
       Default is 4096 per runtime. A value of 0 disables JIT, -1 disables max.
    jruby.jit.maxsize=
       Set the maximum full-class byte size allowed for jitted methods. Default is 10000.
    jruby.jit.logging=true|false
       Enable JIT logging (reports successful compilation). Default is false
    jruby.jit.logging.verbose=true|false
       Enable verbose JIT logging (reports failed compilation). Default is false
    jruby.jit.logEvery=
       Log a message every n methods JIT compiled. Default is 0 (off).
    jruby.jit.exclude=
       Exclude methods from JIT by class/module short name, c/m::method_name,
       or -::method_name for anon/singleton classes/modules. Comma-delimited.
    jruby.jit.cache=true|false
       Cache jitted method in-memory bodies across runtimes and loads. Default is true.
    jruby.jit.codeCache=
       Save jitted methods to  as they're compiled, for future runs.

NATIVE SUPPORT:
    jruby.native.enabled=true|false
       Enable/disable native extensions (like JNA for non-Java APIs; Default is true
       (This affects all JRuby instances in a given JVM)
    jruby.native.verbose=true|false
       Enable verbose logging of native extension loading. Default is false.
    jruby.fork.enabled=true|false
       (EXPERIMENTAL, maybe dangerous) Enable fork(2) on platforms that support it.

THREAD POOLING:
    jruby.thread.pool.enabled=true|false
       Enable reuse of native backing threads via a thread pool. Default is false.
    jruby.thread.pool.min=
       The minimum number of threads to keep alive in the pool. Default is 0.
    jruby.thread.pool.max=
       The maximum number of threads to allow in the pool. Default is unlimited.
    jruby.thread.pool.ttl=

これらのオプションは

http://kenai.com/projects/jruby/pages/JRubyOptions

に一覧を作っておいてあるので、ざざっと眺めてくださいませ。この一覧はRedBridge(JRuby Embed)にコンフィグ用のメソッドをいろいろ追加するときに、ソースコードを眺めつつまとめたものです。とにかくやたらたくさんあって、なにがどうだったか忘れてしまうので。ただ、すべてのオプションがまともに動くワケではなくて、かなーりexperimentalなオプションも混じっているらしいので、中には"効かん!!"というのもあるかも。

オプションを改めて眺めるとよーくわかるのが、JRubyはやっぱりJavaのアプリケーションなんだということ。Javaシステムプロパティやコマンドラインオプションを使ってJVMのコンフィグをする、というJavaな人には当たり前すぎる方法、でもRubyな人には未知の世界(?)な方法がいくつもあります。これらJavaなオプション使いこなしはJRuby使いこなし、スピードアップのためには必須なので、マスターすべし。だって、思ったでしょ。Hello Worldirb起動して、"JRuby遅ぇ"って、MRIに比べたら。:)


その4 JRubyスピードアップのオプション
JRubyの名誉のため、念のため、言っておこうと思いますが、JRubyはそれなりのアプリケーションを動かす上では実行速度という点でMRIに決して引けはとりません(きっぱり)。起動は確かにMRIに比べて遅いけれど、一度立ち上がってしまえば早いんです。このあたりはHello Worldじゃわかりませんから。

それでもやっぱり実行速度は速いにこしたことはないし、"もっと速く!"という願いは不変なので、チューンアップ方法をいくつか。

Javaな人にはお馴染み、JVMにはclientモードとserverモードがありますよね。ぜひ、clientモードでお試しを。これは私自身も経験したのですが、、、、なんだかJDK 6だとミョウに遅い。JDK 5ならもっと速いのに、、、何だこの違いは?!と思ったら、JDK 5はclientがデフォルトでJDK 6はserverがデフォルトになっているのでした。その後、client/serverでのパフォーマンスの違いはIRCでも話題になっていて、"やっぱりね"と思った。この話しはCharles Nutter氏のブログ

Headius: JRuby Startup Time Tips

にも登場。ちなみに私のOS Xで試したら、

time jruby -J-server -ve "require 'rubygems'; require 'columnize'"
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Server VM 1.5.0_24) [i386-java]

real	0m1.741s
user	0m2.214s
sys	0m0.208s

time jruby -J-client -ve "require 'rubygems'; require 'columnize'"
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.5.0_24) [i386-java]

real	0m1.315s
user	0m1.090s
sys	0m0.149s

な感じで、やっぱりclientを選ぶと速い。ところで、OS XJDK 6だと -clientを指定しても常にserver JVMでしか起動しないのはバグなのかしら?UbuntuのOpenJDK 6はちゃんと切り替わったけれど。。。

  • JITはOFFの方がいいときがある

Nutter氏のブログにもあるチューンナップから、もう一つ。JRubyJIT compilerはOFFにした方が速い場合がある。なぜかというと、compileのプロセスが遅いから。JITが効きそうなプログラムの実行ならJITをONにしておいた方がみたいだけれど、そうじゃないときはOFFのほうがいいかも。-X-CでOFFにできます。ただ、"require 'rubygems'; require 'columnize'"の程度だと違いはびみょー。

  • ObjectSpaceをOFFにすると速くなるかも

次はこちらにあるチューンアップから。

http://kenai.com/projects/jruby/pages/PerformanceTuning

ObjectSpaceというもの、私はあまり使ったことがないのですが、Rubyな人はよく使うのかな?「Ruby逆引きレシピ」だと、レシピ198の”オブジェクトが存在しているかを調べたい”で使ってますね。ま、必要無い場合、JRubyは-X-Oで無効にできます。さっきのJIT OFFと一緒に指定すると、、、

time jruby -J-client -X-C -X-O -ve "require 'rubygems'; require 'columnize'"
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.5.0_24) [i386-java]

real	0m1.217s
user	0m1.041s
sys	0m0.141s

気持ち、速くなっているかも。ちなみに、Performance TuningのドキュメントにあるThread Poolingの機能はアプリケーションによりけり、concurrentでびしばしさばいていかないといけないアプリじゃないと。この辺はJavaのマルチスレッドのノウハウを調べてから使った方がいいかも。--fastオプションも場合によりけり。遅くなることもあって、ビミョーなオプション。

  • とりあえず -Xmx, -Xms。効くかも。

Java使いにとってはいにしえからのお友達です。heapサイズの最大、最小値を設定するJVMオプションですね。特に-Xms、最小値の方はstartup timeに効くはずなので、立ち上がりが速くなるはず。。。32MBを指定して試してみると、、、

time jruby -J-client -J-Xms32m -X-C -X-O -ve "require 'rubygems'; require 'columnize'"
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.5.0_24) [i386-java]

real	0m1.167s
user	0m0.978s
sys	0m0.138s

まったくオプション無し(-J-clientはディフォルトだから付けていない)の

time jruby -ve "require 'rubygems'; require 'columnize'"
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.5.0_24) [i386-java]

real	0m1.320s
user	0m1.090s
sys	0m0.148s

と比べると、速くなっているよね。たったこれだけのプログラムだから、違いはわずかだけれど。

JVMのオプションは
http://performance.netbeans.org/howto/jvmswitches/index.html
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
なんていうのがあるし、Nutter氏は
Headius: My Favorite Hotspot JVM Flags
なんていうオタクなブログも書いているし、まだいろいろチューニングできそう。試してください、いろいろ。


ここでとりあげた以外にも、Nailgunを使う方法とか、Rubyのクラスをコンパイルしてしまう(http://kenai.com/projects/jruby/pages/JRubyCompiler)とか、JRubyには実行速度カイゼン方法がまだまだあります。よりどりみどり。いずれ、そのあたりも書きたいなぁ、と思う。



ところで、Nutter氏の"Startup Time Tips"、「近いうちに日本語訳します」って@hiro_asariが3月はじめにtweatしていたんだけれど、どこかで公開されているのかしら?”訳しました”とか”公開しました”とかいうのは見かけていないような‥

[追記]
日本語訳がようやく出たようです。