プログラムの動作が遅くて所期の目的を達成しづらい、ということはよくあります。ただ、最適化もやるべき所でやるべきことをきちんとわきまえないと、労多くして実りが少ないことにもなってしまいます。
そもそも、最適化って?
まず、「最適化」で国語辞典を引いてみると、「システム工学などで、特定の目的に最適の計画・システムを設計すること。」とあります。つまり、プログラム構造より上の、ネットワークトポロジーや機器構成などを「最適化」するということも考えられます。とはいえ、そこまで考えていると話が大きくなりすぎるので、今回はプログラムレベルでの最適化について主に考えます。
何を最適化するか
そして、プログラムの動作を「最適化」すると言っても、いろんな側面が考えられます。
- 動作速度を上げる
- プログラムサイズを小さくする
- プログラムの消費メモリを少なくする
- プログラムの消費電力を減らす
もちろん、「まったく無駄な処理を減らす」というように、いずれも改善するような最適化ももちろんあります。ただ、ものによってはトレードオフとなることがあります。たとえば、HTTPには「データを圧縮して転送する」という機能があります。これを行うことで通信容量は削減できますが、圧縮・解凍を行う処理が必要となるので、計算量は増加します(もっとも、多くの場合に通信量が増えることによる負荷のほうが圧倒的に大きくなるので、これが最適化として成立します)。また、よく「また必要になる値をキャッシュしておいて、後の計算を省略する」ということが行われますが、最近のCPUはメモリよりずっと速くなってしまったので、状況によっては「すでにレジスタにある値から必要な値を再計算する」ほうが効率的になってしまうこともあります。
最適化のトレードオフ
上のように、「あっちを立てればこっちが立たない」ような最適化もありますが、最適化以外の部分とのトレードオフも存在します。
- 最適化したコードは読解しづらく、また修正もやりづらい
- 特定の条件に合わせて最適化した場合、それ以外の条件で性能が低下する、あるいはまったく動作しなくなる
- 最適化の過程でバグを作りこむこともある
- 最適化自体にも時間がかかる
そんな訳で、最適化は「すればするほどいい」というものでは決してありません。
ステップに応じた最適化を考えてみる
「人生はひと箱のマッチ箱に似ている。重大に扱うには馬鹿馬鹿しい。重大に扱わなければ危険である」なんて言葉がありますが、最適化にも似たようなことが言えます。プログラムを書く最初から細かな「最適化」をしていてもデメリットしかありませんが、逆に動作性能のことを何も考えずにプログラムを作ってしまえば、あとから修正するのもまた困難な話です。
設計レベルでの検討
プログラムを書き始める前に、(たとえ書類に落とさないとしても)本来のプログラムをいきなり書きだすのではなくて、ある程度設計を行うことも多いと思います。その時に性能面と関連して重要になってくるのが、データ構造とアルゴリズムです。
データ構造
RDBMSを考えてもらうとわかりやすいですが、データ構造をどのように取るかで、どんなことがすんなりできるかが変化してきます。たとえば、カンマ区切りでデータを1行に詰めてしまうと、あとあとの集計で困難を来たします。
もちろん、理想的な形でデータを入れても速度が出ない、ということも考えられますが、そんな場合は適宜データをキャッシュさせて解決しましょう。元のデータ構造を曲げてしまうと扱いが面倒になります。
アルゴリズム
小手先の改善で速度を2倍に上げることはそう簡単ではありませんが、アルゴリズムを変えることで一気に10倍以上高速化できることもあります。幼き日のガウスは「1から100まで全部足してみましょう」と言われた時に、等差数列の和の公式を使って、「5050」と即答したとのことですが、そのように計算の手間がアルゴリズムによって圧倒的に異なってくることがあります。
コーディングレベルで考えること
実際にコーディングしていく上でも、少し意識を持っておくと、効率的なプログラムを効率的に作ることができます。
標準的な関数を使う
まずは、言語にある標準ライブラリを確認して、それで可能なことは積極的に使って行きましょう。すでにあるのを自分で書いても、それより性能が向上することはそうそうありませんし(ただ、ループ系でコールバックをとる関数だと、コールバックのオーバーヘッドで負けるということも時にはあります)、新たにバグを作りこんでしまう危険もあります。標準関数を使っておけばそんな心配もないですし、バージョンアップで高速化するかもしれません。言語によっては、ネイティブ実装になっていて、言語内では速度的に太刀打ちできない、ということもあります。
また、行列演算のBLAS、3DグラフィックのOpenGLのように著名なライブラリがある分野であれば、それを使ってしまうのも一案です。とりわけ、BLASなどはCPUメーカーが極限までチューニングしているバージョンを出していることもあって、一般プログラマーが手で書いたのでは到底叶わない性能を発揮します。
最適化フェーズにて
動作するコードが書き上がったけど、性能が足りないとなれば、本腰を入れて最適化する必要があります。何はともあれ、まずは計測です。思っていた以上に、意外な場所で時間を食っていることが多々あります。逆に、もともと1ミリ秒しかかからない処理は、どれだけ努力しても1ミリ秒以上高速化することはできません。
実際に行う「最適化」と言っても、多くの場合は無駄な処理の削減などできれいに書ける範囲内でも効果があることが多いです。また、このように、特定の環境が持つ機能を活かすことで、高速化できることもあります。