NTTドコモのiアプリに代表されるように、組み込み用途でJavaが広がっている。それに伴い、組み込み用途でのJavaの高速化が注目されている。そこで、Javaの高速化手法をプロセッサの視点からのぞいてみよう。
今回の話はJavaである。というとソフトウェアの話題だと思われるかもしれないが、ハードウェアの話だ。ご存じのとおり、NTTドコモがiアプリというサービスを始めて、携帯電話でもJavaが走る(実行できる)ようになった(NTTドコモの「iモード対応Javaに関するホームページ」)。ちょっと前までは、次世代携帯でMPEG-4によるテレビ電話、というのがまずハードウェア技術者の注目するところだった(今でも注目しているはずだが)。だが、W-CDMAのインフラからしても、本格的にテレビ電話が可能な端末の台数が増えるのには、まだしばらくかかりそうだ。そういった現実から、MPEG-4を追い越してJavaが携帯電話の世界に乱入し、一足先に火がついたというところであろう。
お陰でエンベデット(Embedded:組み込み用途)の世界でのJavaは一気に普及しそうである。携帯電話にとどまらず、エアコンから冷蔵庫まで「Javaが走らなければ、電気製品にあらず」といった状況が本気で考えられている昨今なのだ。ともかく、Javaを走らせないといけない。でも、走らせたら次はハードウェア技術者の得てして陥りやすい大競争が始まる。カフェインマーク*1競争とでもいったらいいのだろうか? うちのハードウェアはこんなに速い、というのを競わないとならないのである。何を走らせるかというところは出る幕でなく、そのくらいしかハードウェア技術者の付加価値はなくなっているのだ。
*1 カフェインマーク(CaffeineMark)とは、Javaのパフォーマンス測定に利用される標準的なベンチマーク・テスト・プログラムの1つである。
しかし、よく考えてみるとすぐに分かることだが、組み込み用途で使えるプロセッサは安い。出始めたばかりのPentium 4と比べて、下手をすると100分の1くらいの値段の安いプロセッサでJavaを走らせなければならない。安いプロセッサは当然遅いし、そのうえ携帯機器では電力量の限られた電池で動かさなければならないという制約もある。動作クロックを引き上げて、Java VM(JVM:Java仮想マシン)を走らせればよいというわけにはいかないのだ。
すると、「JITコンパイラ(Just In Timeコンパイラ)を使えばイイジャン」などという意見が出る。これは、ありがちな展開なのだが、それはそれで問題が出てくる。組み込み用途ではメモリの容量が限られている。増設などはできないのが普通だ。メモリの価格が安くなった昨今、PCならば256Mbytesくらい搭載しても驚くほどではない。しかし、組み込み用途となると、コストや実装面積の制限などから、使えるメモリ容量は数Kbytes、数Mbytesのすごく限られたリソースでやりくりすることになる。何せハードディスクのような外部記憶装置もないので、組み込み用途のメモリはメイン・メモリとしてだけでなく、ファイル格納領域の機能も兼ねることになる。というわけでJITは現実的ではない。JITコンパイラを走らせるために結構なメモリが必要になるからだ。
というわけで、Javaということでいったんソフトウェア側に渡ったかに見える主導権が、また巡り巡ってハードウェア側の話に戻ってくるのである。このあたり、ハードウェア技術者としては少しうれしい。安く非力なプロセッサで、Javaをガンガン走らせるために、値段の安い(要は回路規模が小さいということだが)ハードウェアでJavaをアクセラレートしてやろうという発想だ。ソフトウェアの高速化手法としてJITコンパイラがあるように、ハードウェアによるJavaのアクセラレートにも何種類か手法がある。
第1の手法は、Javaの御本家Sun MicrosystemsのJavaプロセッサに代表されるように、Java専用のプロセッサを作るという方法だ。だが、このところJavaプロセッサについては、ほとんど話題がないように、Javaのバイト・コードを実行するスタック・マシン*2というコンセプトは、すでに滅んで久しい。ということで、この手法はあんまりうまくいかないだろう。個人的にはスタック・マシンは大好きなのだが、多くのRISCの教科書に書いてあるとおり、明示的にオペランド(データの場所を示すアドレス情報)をとらず、スタック・トップに操作が集中するスタック・マシンの高速化は難しいのである。性能を発揮させようと思うと、普通のRISCを作るのに比べても難しいことになる。それにJavaはいいが、ほかのプログラム・コードは別のプロセッサなどに押し付けなければならず、どちらが主体になるのか調停も面倒になりそうだ。
*2 スタックとは、LIFO(Last-In First-Out)型、つまり最後に入れたものを最初に取り出すバッファのこと。スタックに2、3、4というデータをこの順番で入れた(積み込んだ)場合、取り出すときは4、3、2という順番になる(最後に入れたものが最初に取り出される)。このスタックの仕組みを使って計算を行うのがスタック・マシンである。例えば、2+3を計算するような場合、2と3を順番にバッファに入れ、その後にスタックの一番上から2つの数の和を計算する命令を実行する。すると、スタックから2つのデータ(2と3)が取り出され、代わりに和の5がスタックに入れられる。スタック・マシンのメリットは、データの位置(アドレス)を示すのにスタックのポインタ1つだけで済むため、構造が簡単であることが挙げられる。
第2の手法は、Javaバイト・コードを実行するためのコプロセッサという、一歩引いたアイデアである。バイト・コードが現れたときだけ、横にいるスタック・マシンに実行してもらう。その間、メインのRISCはお休みとなる。第1の手法とあまり変わらない感じもするが、コプロセッサということで、一応メイン・プロセッサに寄生する作りにして、かなりの部分、メイン・プロセッサの機能を使わせてもらう。第1の手法で問題だった入出力やプロセッサ間の調停もすっきりする。メインのRISC上のJVMでJavaを走らせるよりも速そうだし、Javaを実行するためのオーバーヘッドも第1の手法よりは小さそうだ。だが、やはり前時代的なスタック・マシンが鎮座していることには変わりはない。
ここへきて注目を集めているのは、第3の手法である。Javaバイト・コードをメイン・プロセッサのネイティブ・コードに変換する「トランスレータ」ハードウェアを実装するというものである。トランスレータによって、スタックを前提にしたJavaバイト・コードは、「普通のプロセッサ」が持つレジスタ・ファイル上での操作に、ハードウェア・レベルで変換される。また、トランスレータはメイン・プロセッサの命令フェッチのルートに仕込んでおけばよいので、メイン・プロセッサのキャッシュやメモリ・インターフェイスなどに変更は不要だ。JITコンパイラのハードウェア版といえるかもしれない。ところが当然、メイン・プロセッサは、Javaを念頭に設計されたものではないだろうから、スタックとして使えるレジスタ本数などが限られる。スタックのオーバー・フローやアンダー・フローなどで、レジスタがメモリへアクセスする回数などは、第1や第2の手法のアイデアに比べると多くなるだろう。このあたりに目をつぶっても、軽いトランスレータは、メイン・プロセッサそのものの性能で動くので魅力がある。
すでに何社かのベンチャー企業が、それぞれのやり方のJavaアクセラレータを手に熱心に商売を行っている。「カフェインマーク値」に敏感なオジサンたちは、他社製品に水をあけようと、これらのJavaアクセラレータの購入を早速決めたという。一方で、いまだにどれを買えばいいのか迷っている人もいるようだ。ただ、どこの会社のJavaアクセラレータも各種プロセッサ対応とうたっている割には、決まって最初のターゲットはARMである。それだけ携帯の世界でのARMの存在感が際立っているということでもある。しかし困ったことに、商売には極めて抜け目のないARM社は、自ら第3の手法を採用したJavaアクセラレータを開発中だ。ARMのJavaアクセラレータが市場に出てくる前に、どれだけ市場を獲得できるか、あるいはARMのものよりどれだけ性能を上げられるか、先行するベンチャー各社には勝負どころかもしれない。
P.S. しかしJavaよ、ヒロスエがインベーダーやらパックマンやらこれまた亡霊のようなゲームをやっているだけでいいのか? もっと考えてくれないか!
日本では数少ないx86プロセッサのアーキテクト。某米国半導体メーカーで8bitと16bitの、日本のベンチャー企業でx86互換プロセッサの設計に従事する。その後、出版社の半導体事業部を経て、現在は某半導体メーカーでRISCプロセッサを中心とした開発を行っている。
「頭脳放談」
Copyright© Digital Advantage Corp. All Rights Reserved.