ネイティブコードの実力

C言語で書けば何でも速い、というのはたぶん幻想だと思っています。少なくとも、今までいろいろ作ってきた限りでは、まともな実行環境ではバイトコードVMでも、元からネイティブコードでも大して違いはないと思います。
と、言う話を今日は。
あくまで感触を語る雑談なので、数値を上げたベンチマークはありません。話半分に読んでください。


以前、大容量データを使って、画面になにやらレンダリングするプログラムを作っていました。まずは動くかどうかを調べるためにJava6で、アルゴリズムに裏付けができたらFlex2で。
なにせ、Java Appletはまずは一般人はあまり使わない(らしい。何でかなー)JREのインストールが必須で、そのうえ大メモリを使おうとするとあらかじめVMの起動オプションを指定しないとうまく動いてくれないので、エンドユーザー向けにはほぼ使えないのです。Java自体はいい環境なんだけどなー。
ともあれ、JavaからActionScript3.0に処理を移植するために、スレッドを使っていたところをコールバックとポーリングに直した上で、あとは愚直に変換していきました。変数や関数の記述方法と組み込みクラスの機能こそ違いますが、なるべく組み込みクラスを使わずに何とかする方針でJava版を作っていたので、かなり移植自体はスムースに行きました。
まずは、愚直に移植版は、かなりパフォーマンスに問題がありました。
SwingのMetalスタイルに比べて、Flexのディフォルトスタイルはかなり綺麗なので、ぱっと見の見かけはだいぶ印象良かったのですが、いかんせん、当のレンダリング自体は今一つのパフォーマンスでした。
そこで、以下の手順をとりました。

  • Javaでfloatで計算していた所を固定小数点にして検証
  • ActionScriptにそれを移植

Java版は劇的に速くなりました。いまいち解せませんがActionScript版は何となく良くなったかなってぐらい。
次に、

  • Javaで計算に使っていたヴェクタクラスを、基本型のローカル変数に書き直す
  • ActionScriptにそれを移植

こちらはJavaではほとんど差がなく、ActionScriptでは劇的にスピードアップ。
何となく原因がメソッド呼び出しのコストのような気がしてきたので、とうとうレンダリングルーチンの中ではオブジェクトを全く使わず、基本型と最低限の組み込みクラス(ByteStream)だけで、プライベートメソッドすらも呼び出さないようにすると、ちょうどJavaと同等のパフォーマンスになりました。
それまでの展開から見て、たぶんJavaで同じことをしてもほとんどパフォーマンスに影響がないと思います。というより、苦労の割に実入りが少ないと思います。Javaのメソッド呼び出しはかなり最適化されているようなので、きっとそこにはボトルネックがないのでしょう。
それに比べてActionScript3.0は、かなり静的型にコンパイル時はうるさい割に、メソッド呼び出しをする際には、かなりオーバーヘッドがあるようです。
むしろ、気にするべきは両方とも実行環境のJITコンパイルがかなり強力で、少なくともメソッド内での動作に関しては、パフォーマンスを見る限りではほぼ同じものとして見なせるっぽいと言うところです。
と、いうことは。
極度に進歩した実行環境においては、全く同じアルゴリズムで実装されている限り言語の違いというのは実は何の影響も及ぼさないという可能性が非常に高いです。
特に、今回の実行結果を見る限り(Flashの実行環境のプロファイルって厳密にはどうすればいいんだろう(^^;)?)、

  • メソッド内でのJIT後の性能に関してはほぼ同等
  • メソッド呼び出しはJavaの圧勝
  • 基本型演算ではCPUの能力に依存(floatがCore2においてそんなに遅いとも思えないんだけど……)

その上で、今回のプログラム以前の話として、

  • Java(-serverオプション使用時)とCのパフォーマンスはほぼ同等

という前提から考えると、

  • アプリケーションのパフォーマンスは、CPUの能力にのみ依存し、言語環境には依存しない

という、何というか非常に当たり前な結論に落ち着くのでした。


まぁ、それぞれの言語においてどこまで高レベルな部分での最適化が行われているのかは実は判っていないのですが(あ、Javaバイトコードまでの部分では、ほとんど最適化されてないように見えます。逆コンパイルした限りでは)、今回のプログラムにおいては純粋に大容量データ内を走査するだけのプログラムでしたので、高レベルに見なせる部分なんかほとんどないとは思います。
と言うことは、どんな言語であっても、JavaVMかFlashのランタイムに落とし込むことができれば、ほぼマシンの持つ処理限界性能までは使えるんじゃなかろうか、という幻想的な世界がかいま見える今日この頃です。
さて、今度は昨日Squeak関連のイベントで話を聞いたScalaJRubyでも試してみるかなぁ。これで同じパフォーマンスだったら、特定の言語を選ぶ、もしくは選ばない理由にパフォーマンスなんてのはもう時代遅れと言い切れるんだけど。
まぁ、.Net(CLR)とJavaのパフォーマンスが今までさわった限りでは同等っぽいので、言い切っちゃってもさほど問題なさげではあるんですが。.Net環境ではこれまたいろんな言語が動いてるしなぁ。