コード探偵 ファイル06 「暗闇にさす光」
とあるプロジェクトで処理落ち問題が発生し、最適化をすることになりました。二人の技術者が同じ時間を掛けてプログラムの違う箇所の最適化を行い、その結果、二人の技術者は以下のように報告しました。
技術者A「俺はAの部分を5倍速くしたよ」
技術者B「僕はBの部分を2倍の速度に最適化しました」
さて、仮にあなたがこのプロジェクトの管理者だとして、どちらか一人の技術者に成功報酬をあげるとしたら、どっちの技術者を選びますか?
もしAかBのどちらかをこれだけの質問で選ぶことができたら、管理能力に難ありです。どっちも選ばずに自分の懐に入れると思った人は管理者というよりも人としてなにか間違っているでしょう。
まともな管理者であれば「もっと情報が必要」とか「プロジェクト全体の貢献度による」と答えるでしょう。
ここで陥りやすいのは数字のマジックです。人間はどうしても「2倍」、「5倍」という数字を聞くと、その印象が強く残ってしまいます。
ですが、ここで重要なのは「プロジェクト全体にどれだけの影響を与えたか」ということです。
仮にAの部分がプロジェクト処理時間の20%、Bの部分が80%使っていたとします。この場合、Aの部分を5倍速くした場合、プロジェクト全体の処理時間としてみると84%になるので、約1.2倍のパフォーマンスアップになります。次にBの部分を2倍速くした場合、プロジェクト全体で見ると約1.7倍のパフォーマンスアップになるわけです。
要するに、最適化をする場合は常にボトルネックとなっている部分を最適化するのが効率的であるということになります。このことをアムダールの法則と言います。
余談になりますが、「何倍速くした」というのはセールストークで、SSE/VMXになって四つの演算が同時にできる!とか、6コアだから爆速!みたいな使われ方をしますが、実際のプロジェクト全体の速度が4倍になったり、6倍になったりすることは無いのが現実です。逆にセールストークを鵜呑みにして安易に使おうとすると、作業量に見合わない程度のパフォーマンスアップにしかならなくてガッカリで済むならまだしも、逆にパフォーマンスが出なくなったなんて言うこともありえるので注意が必要です。
賢く最適化
え?両方の最適化をすれば、2.3倍になるだろうですって?
確かにそうですが、それは人材と時間と予算が許せばそういうことも可能でしょう。ですが、大きなゲーム開発スタジオでさえ常にそれらのリソースを確保するのに苦労していますし、ましてやリソースに限りのあるインディーズゲーム開発では、リソースは賢く使う必要があります。
また、ここでの目標はどれだけパフォーマンスアップできるかではなく、「処理落ちしない」ようにすることです。ですから、5倍の最適化が無駄だと言っているようにも見えますが、必ずしもそうではありません。1.2倍の速度にして処理落ちがなくなるのであれば、この最適化を施したとしても目的は達成できる訳です。ただし、どう考えてもボトルネックになっていない箇所に貴重な時間を割いて最適化するよりも、ボトルネックとなっている部分を最適化する方が少ない労力で大きな効果を得られることが遥かに多いということです。
まとめ
さて、複数に渡ってパフォーマンスを殺した犯人(CPUかGPU)とその方法(ボトルネック)の発見の仕方を紹介してきました。これらを簡単にまとめると以下のようになります。
処理落ちしたときにやるべきこと
- プロファイルの準備をする
- 現在のFPSを記録する(後でどれだけ早くなったか調べるため)
- CPUバウンドなのか、GPUバウンドなのかを調べる
- CPUバウンドであれば、タイムルーラー(Stopwatchでも可)を使って二分探索ですばやくボトルネックを探す
- 6へ進む
- GPUバウンドであれば、パイプラインの特性を利用してボトルネックを探す
- ボトルネック部分のコードをバックアップする(後で元に戻せるようにするため)
- ボトルネック部分を最適化する
- まだ遅い場合は2に戻る、もしくは9に進む
- 妥協する(時には必要)
- キャラクターやモデルの表示数を少なくする
- フレームレートを下げる
- 2に戻る
処理落ちした時にやってはいけないこと
- 勘でボトルネックになりそうなところをやみくもに最適化する
- 測定せずに最適化をする(どれだけ速くなったか判らなくなる)
- 目指すパフォーマンスには到底届かないという事実を頑なに否定する(リリースできなくなる)
- 現実逃避(息抜きには良いけど)