【Effective Java】項目55:注意して最適化する

最適化に関してはさまざまな格言があります。 特に知られているのは Knuth によるものでしょう。*1

僅かな効率、たとえば、時間の約97%、については忘れるべきである。時期尚早の最適化は、すべての悪の根源である。

速いプログラムを書くことよりも、良いプログラムを書くように努めるべきです。 良いプログラムは必要になれば速くすることできます。

これは完成するまでパフォーマンスを無視してよいということではありません。 パフォーマンスを低下させる要因となる設計の欠陥は、後から修正することはほぼ不可能だからです。

必ず設計段階でパフォーマンスについて考えるべきです。

API 設計とパフォーマンス

API 設計時には必ずパフォーマンスを考慮してください。

  • public クラスを可変として設計すると防御的コピー(項目39)が必要になる場合があります
  • 継承を利用すると、そのクラスはスーパークラスのパフォーマンスに制限されます
  • API においてインタフェース型ではなく実装型を利用していると、あとからより速い実装に取り替えることが難しくなります

java.awt.Component クラスの getSize() メソッドは毎回 Dimension インスタンスを生成しているため、何度も利用するとパフォーマンスが劣化します。 本来は Dimension を不変にするか、基本データ型を返す2つのメソッドを作るべきでした。

良い API 設計は良いパフォーマンスと矛盾しません。 良いパフォーマンスを達成するために API をねじ曲げる必要はありませんし、そうするべきではありません。

プロファイリングの必要性

注意深く設計しシンプルに実装した結果、パフォーマンスに満足できない場合に始めて最適化を検討するべきです。

この時に必ず最適化の前後のパフォーマンスを測定してください。 パフォーマンスを測定すると最適化に効果がないことが分かることも多いです。

Java は従来のプラットフォームよりも最適化の効果を特定する必要が大きいプラットフォームです。 プログラマが書いたプログラムと、実際に CPU が実行する命令との間の「意味的ギャップ」が従来のコンパイラ言語よりも大きいためです。 そのため、プログラムから実際のパフォーマンスを推測することは非常に困難です。

唯一可能なのは実際に計測することです。 複数の JVM 実装、同じ JVM 実装の異なるプラットフォームでパフォーマンスが劇的に異なる可能性があります。

もし、最適化が必要だと分かったら、まずアルゴリズムの変更を検討してしてください。 O(n2) のアルゴリズムはどこまでチューニングしても O(n2) です。 より効率的なアルゴリズムに変更すれば細かなチューニングをするよりも劇的に速くなる可能性があります。

*1: 2016/07/24追記

どうやら後半の「時期尚早の最適化は、すべての悪の根源である。」という部分は Knuth のオリジナルではないようだ

hans.gerwitz.com