「Java FAQ(What's New)」の安藤幸央氏が、CoolなプログラミングのためのノウハウやTIPS、筆者の経験などを「Rundown」(駆け足の要点説明)でお届けします。(編集局)
コンピュータの世界には「10−90の法則(90/10 Rule」と呼ばれる法則がある。たいていのプログラムの場合、プログラムの一部分(約10%)がほとんどの処理時間(約90%)を消費しているという法則だ。つまり、その一部分の処理にかかっている時間を高速化すれば、プログラム全体の動作速度が飛躍的に向上することを暗示している。
プログラムは「前処理」「エラー処理」「後処理」、そして「主要部分」といくつかの工程に分かれる。一度しか処理を行わない部分は、速度よりも分かりやすさを重視したプログラミングを行うのが適当だ。反対に、何回も同じ処理を繰り返すような部分は、パフォーマンスチューニングの結果が全体の結果として顕著に表れてくる。
また、デスクトップパソコンなどのマシンパワーの加速度的な増加とともに、メモリ効率、動作効率などはあまり意識せずとも快適な環境が得られやすくなってきた。けれどもその環境に甘んじていてもよいのだろうか?
確かに、環境の制限にとらわれない理想的なシステムを作り上げるためには、細かい制限によってアイデアまでもが制限されてしまってはいけない。過去の遺産を引きずることなく自由な発想で考えることが必要であるが、実際に「使われる」プログラムにおいてはそうとばかりもいっていられないのが現状だ。限られた資源で最大のパフォーマンスを出すのも、エンジニアとしての力量である。
今日では携帯電話上でのJava環境が数々登場してきている。組み込みデバイスという限られたメモリサイズ、限られたマシンパワー、限られた画面表示領域の中で、いかに面白い、いかに有益なプログラムを作るのかが再び注目されるようになってきた。ここで紹介するパフォーマンスチューニングの心構えは携帯電話用のJavaアプリケーションにおいても大いに活用できるであろう。
パフォーマンスチューニングの前にまず大切なことは、
「効率の良いアルゴリズム、効率の良いデータ構造をじっくりと考えること」
ここでつまずいていては、最終的にどんなにチューニングをしてもよい結果が得られない場合がある。データ構造の単純化は、思わぬ効果を上げることがある。そして、データ構造の変更は、ソフトウェアが完成してしまってからでは難しくなってしまう。プログラムを作り上げるまで判明しないのがパフォーマンスだと思い込んでしまうのではなく、小さなテスト用のプロトタイプでテストはいくらでも可能だ。まずは理想とする、チューニングの必要のないプログラム作成を目指すべきである。
速度に大きな犠牲をもたらしてしまうような、行き過ぎた高精度なデータを扱う必要はない。必要十分な精度、マシン環境に最適な精度で扱うことによって、最も効率良く動作させることができる。また、いくつもの精度間を行き来することによる無駄な型変換を生じさせないよう考慮する必要もある。世の中にはJavaScriptのように型をあまり意識しなくても記述できるものもあり、プログラマーにとっては記述しやすく易しい言語だといえる。しかし、このことは逆にコンピュータにとっては易しくないことを忘れてはならない。
ここでいう再利用はソースコードの再利用ではなく、内部的なデータの使いまわし、メモリの使いまわしのことだ。もちろんプログラミングの初期段階から細かくこれらについて考慮する必要はないかもしれないが、チューニングが必要な段階において、一度確保したデータはなるべく確保した段階でさまざまな必要な処理を終わらせてしまうなどといったアプローチが取れるだろう。
一番時間のかかるところはループの部分であることが多い。二重三重の多重ループであればなおさらだ。カウントアップするループよりもカウントダウンしていき、最後の終了条件が0との比較というのが一番効率の良い速いループだ。けれどもそのカウントを利用していたり、何回繰り返すのかが重要なときはそのノウハウは使えない。
フォトリアリスティックなコンピュータグラフィックスの世界では、画面のピクセル1点1点ごとに膨大な計算を行う。たとえ1点の計算が比較的短時間で終えられるとしてもフルスクリーン1画面の計算には数多くの計算を繰り返さなければいけない。この例からも明白なように、ループの中で何を繰り返し行っているかに注目することは、チューニングにおいて重要である。
条件分岐は、最も起こる確率の高いものから、先に判定すべきである。または判別に時間のかからないものから判定していくべきだ。同じくフォトリアリスティックなコンピュータグラフィックスの世界では、さまざまな条件下のものに分岐していき、いかに計算を省くかが重要となってくる。
例えばHotSpot 1.0には必要だったウォーミングアップ・ループ(暖気運転のようなもの)がHotSpot 2.0では必要ない。ちょっとしたパフォーマンスに関するコツやノウハウを知っているか否かによって、大きく効率が変わってくる一例である。
以上に説明したように、開発の初期段階では、設計、データ構造、アルゴリズムといった大枠に力を入れるべきだ。開発の初期の段階でのプログラムのチューニング(最適化)は無意味とは言い切れないが、効果は小さいだろう。最適化は最初に行う作業ではなく、最後に行う作業であることを忘れてはならない。
まずは適切なテスト環境、テスト条件を用意しよう。「こうすれば速いハズだ」と思い込んではいけない。チューニングはまずボトルネックを調べるところから始まる。むやみやたらにあちこちを高速化しようとしても、無駄な労力を消費するだけだ。
各言語には、どの部分に時間がかかっていて、どの部分の実行回数が多いかをプロファイリングするプロファイラというツールが存在する。これらのツールを用いてプロファイリングした後に、一番ネックになっている部分をチューニングすることによって、最少の作業で最大の効果を得ることができる。
さらには、ユーザーが使用するマシンと、開発で使用するマシンのスピードの差についても考慮すべきだ。当然高速なマシンで開発を行った方が効率が良いのは確かである。そのため工夫や効率といったものを軽く考えがちになる開発機と、ユーザー機との間にスペックの差がある場合はそのこと自体を忘れてはいけない。本番に近い環境でテストするのが大切だ。さらにチューニング結果がすべてのプラットフォームで有効だと思ってはいけない。
また、よほどのことがない限り、動作する環境を限定する作り方はよくない。将来スケールアップするかもしれないし、よりパワーのあるマシン環境に載せかえることがあるかもしれない。
コンピュータの世界は日進月歩で進化する。苦労してオプティマイズするよりも、ソフトウェアやランタイムのバージョンを最新版にするだけで高速化することもある。基本的には常に最新版を使うよう心掛ける下手なチューニングよりも、コンパイラによるオプティマイズの方がよっぽど効果があったりする場合もないとは言い切れない。最適な開発環境、最適な動作環境を用意することもチューニングの1つである。
予算が許すなら、マシンのスペックを向上させるとか、ハードディスクへのアクセスが多いならメモリを追加するとか、ボトルネックに応じた効率的な環境を用意することも必要だ。人力によるパフォーマンスチューニングよりもハードウェアの拡張によって安価に効率を手に入れることができる場合もあるからだ。
またその際は拡張前と拡張後でどれだけ効果があったかを把握しておき、次回の開発に役立てることも忘れてはいけない。「なんとなく体感速度が上がった感じがする」というだけでは正当な評価とはいえない。
ネックを見つけるコツは、「遅さ」をチェックすることによってどこが遅いかを見つけるよりも、あらかじめ遅そうだと狙いをつけた部分の「処理を行わないテスト」にある。ターゲットとする処理を抜いて実行してみることによって、その部分がどれだけ遅かったのかあらためて把握できるというわけだ。
例えば、Webアプリケーションの速さ、レスポンスを考えるときは、ユーザーがWebページの表示や操作を行ったときの「操作感」を考慮することが大切だ。つまり、ユーザーはどこでそのシステムが遅いと感じるのか? を考えるということだ。いま何の処理をしているのかが分かる、とか、自分のリクエストが相手に的確に伝わったことが認識できる、ということであれば、ある程度時間がかかっても遅いとは感じない。
ここで言いたいのは、素早く動くように見えるアプリケーションであることが重要ということで、必ずしも速く動いている必要はないということだ。この違いはどこにあるのか? 実際にさまざまなサービスを使ってみながら実感してほしい。
「ユーザーにとって的確なものを提供する(サイズ、量、スピード)のが重要だ」
コンピュータグラフィックスの世界では、目に見えるものがすべてその画面に表示されるという流れに行き着くまでにさまざまな過程を経てきている。それぞれの個所でボトルネックが生じ、それぞれの個所でさまざまな処理が行われている。それら各所での処理の特徴や役割を明確に把握し、できるだけ次に続く処理を少なくして渡してあげればいいのだ。
かかるコストと速さ/遅さをてんびんにかけて考え、たとえ処理が遅くても開発期間がとても短くて済むソフトウェア環境を採用したりすることは間違いではない。
つまり大切なのはプライオリティ(優先順位)だ。スピード、堅牢性、安全性、テストのしやすさ、保守性、単純さ、再利用性、ポータビリティ。いま開発しているシステムがどの部分を重要視しているのかを見失ってはいけない。
チューニングに見切りをつけることも大切だ。目標とするしきい値を超える適度なところで手を引くのだ。いつまでもチューニングばかりやっていてもきりがない。時間と費用に余裕があるなら、パフォーマンスチューニングはとても面白い作業だ。だが、速いからといってトリッキーなプログラムに走ってはいけない。
逆にちゃんと動いているものに手を入れて、速く動くけど正確に動かないものにしてしまったり、逆にある条件下では遅くなるものに改悪してしまうことが皆無とは言い切れない。速く動作してもちゃんと動かないシステムは、遅くてもちゃんと動くシステムより劣ることは明白だ。性能(速度)に問題がないときは、無理に速く動くように改造する必要はないということも心に留めておくとよいだろう。
以上、当たり前のようなことを取り上げてきた。ここで取り上げた事象を心の隅に留めつつ効率の良いプログラミングを目指してほしい。最後に、著者を含め有志数人で翻訳を行っている「Java Performance Tuning Newsletter 」を紹介しておこう。このニュースレターは、『Java Performance Tuning』(O'Reilly刊、ISBN 0-596-00015)の著者Jack Shirazi氏が月に一度発行している無料のニュースレターを日本の読者のために翻訳して公開しているものだ。
http://www.hatena.org/JavaPerformanceTuning/
Javaが台頭してきて意識的に変わってきたことは、作る前にまず探すことだ。先人の作り上げた効率の良いノウハウ、コツをどんどん活用していこう。
次回は、使いやすさ「ユーザビリティ」を取り上げたいと思う。
次回は9月1日の公開予定です。
安藤幸央(あんどう ゆきお)
1970年北海道生まれ。現在、株式会社エヌ・ケー・エクサ マルチメディアソリューションセンター所属。フォトリアリスティック3次元コンピュータグラフィックス、リアルタイムグラフィックスやネットワークを利用した各種開発業務に携わる。コンピュータ自動彩色システムや3次元イメージ検索システム大規模データ可視化システム、リアルタイムCG投影システム、建築業界、エンターテインメント向け3次元 CG ソフトの開発、インターネットベースのコンピュータグラフィックスシステムなどを手掛ける。また、Java、Web3D、OpenGL、3DCG の情報源となるWebページをまとめている。
ホームページ:
http://www.gimlay.org/~andoh/java/
所属団体:
OpenGL_Japan (Member)、SIGGRAPH TOKYO (Vice Chairman)
主な著書
「VRML 60分ガイド」(監訳、ソフトバンク)
「これがJava だ! インターネットの新たな主役」(共著、日本経済新聞社)
「The Java3D API仕様」(監修、アスキー)
Copyright © ITmedia, Inc. All Rights Reserved.