JavaVMがリアルマシンにならないわけ

 勉強会のセッションやるために大阪に出向いていたのでついでにOFFにも参加してきた。そのときに @ranha にJavaバイトコードを直接実行できるCPUがあったらどう思うかみたいなことを聞かれた。これがなかなか面白い話題なんだ。

 PentiumとかCore 2とかのCPUってのはレジスタマシンと呼ばれる種類のもので、レジスタと呼ばれるモノの上で演算を行うわけだけど、Java VMスタックマシンと呼ばれる種類のもので、スタック上で演算が行われる。レジスタマシン上でスタックマシンをエミュレートしているような格好で、ダイレクトに動くスタックマシンがあればいいのに、というのはごく自然に思い描くことなんじゃないだろうか。

 実はJavaVM(バーチャルマシン)ではない、JavaリアルマシンのようなCPUというのは実在する。富士通がかつてpicoJavaというチップを世に出していて、直接Javaのマシンコードを実行することができたようだ。が、検索してみるとわかるのだけど、90年代末のころの話であってその後の話題を聞かない。もちろん、当時の僕はこのpicoJavaの話を聞いて目を輝かせていたわけなんだけども。

 ところで、JavaはCより速かった — ありえるえりあなんて話題が出る程度には現行のJavaVMというのは速い。初期のAppletのイメージ*1があるのか遅い遅いと思われているけど実際には相当に速い。感覚的にはCのコードと比較しても、1倍少々程度の速度で動く。2倍を超えるようなことはあまりない。「Javaは遅いから専用チップを作るとか、あるいはコプロセッサ的なチップを作るといいんじゃないか?」という考えは、実は前提であるところの「Javaは遅い」が誤りであるので成り立たない。そんなわけで、莫大な開発費をつっこんでまでJava専用チップを開発する意義がない。

 実行時にネイティブコード、つまりマシン語に置き換えるという技術ではJIT(ジャストインタイム)コンパイラというものと、ホットスポット(HotSpot)と呼ばれるものがあって、JITでは起動時にバイトコードマシン語コンパイルする。これは速そうに思うかもしれないけど、コンパイルのオーバーヘッドがかかるところが難点だ。繰り返しのないプログラムを流した場合、インタープリタ式のほうがJIT方式より速い。だってコンパイルするのに一度バイトコードを総なめにするんだもの。1度舐めるだけならインタープリタで1度だけ舐めてダイレクトに実行する方が速い。

 対してホットスポット技術というのは繰り返し部分を検出して動的にその部分をコンパイルするという、まぁ聞いただけでキチガイじみた技術なんだけど、この恩恵は大きい。JITに比べて滑らかに始動し、ヘビィなところではちゃんとパフォーマンスを叩きだすんだから。そういえばJavaとケンカ別れして作られたC#というかCLRだとJIT方式にこだわってて、むしろ最適化の自由がなくJavaよりもっさりしているというなんだか残念な感じになっちゃってる。*2

 んで、そんなホットスポットなんだけど、これまたリアルにバイトコードを実行するチップとは相性が悪い代物だと思うんだ。先のJavaより速いCの話題は実行時最適化で同期がカットされたような例だけども、そういうことがダイレクトにバイトコードを実行するチップにできるのだろうか、と思うのだよね。

 まぁ、最近のCPUってのはマシン語を直接実行しているわけじゃなくてマイクロコードに直して実行するというマイクロプログラム方式を採用していることが多いから、同じようなことをやれるのかもしれないけど、いかんせんCPUアーキテクチャとかその辺は僕の専門範囲から外れるので詳しいことはよくわからない。

 Javaリアルマシンというのは時代の流れに浮かんだ泡沫(うたかた)のようなものだったのかもしれない。

*1:96年当時の細いネットワーク回線でダウンロードしてブラウザ上で動くAppletはそれはまぁ遅いものだった。近年だとネットワークも高速化したし、クラスローダー周りの工夫もあったり、VM自体の性能が向上したり、マシンそのものが早くなったりで言うほど遅くはない。だけど、事実がどうであれ遅いというイメージが染みついてしまって毛嫌いしている人が多いのが残念だ。

*2:JIT方式に固執すると最適化の自由が少ないとは思うものの、もっさり感についてはJITが故とは言いにくいので削除。CLRの仕様だとHotSpotのような技術を用いる隙がないと思うのだがどうだろうか。