一般に、コードを最適化する頻度とタイミングはいつですか?


13

「通常の」ビジネスプログラミングの最適化手順は、本当に必要になるまでしばしば残されます。つまり、本当に必要になるまで最適化しないでください。

ドナルド・クヌースが言ったことを思い出してください。「私たちは小さな効率を忘れて、約97%の時間を言うべきです。早すぎる最適化はすべての悪の根源です」

努力を無駄にしないように最適化するときはいつですか。メソッドレベルにする必要がありますか?クラスレベル?モジュールレベル?

また、最適化の測定値は何ですか?ダニ?フレームレート?合計時間?

回答:


18

私が働いたところでは、常に複数レベルのプロファイリングを使用しています。問題が発生した場合は、何が起こっているかがわかるまで、リストをさらに下に移動するだけです。

  • 「人間のプロファイラー」、別名ゲームをプレイするだけです。それは時々遅く感じたり、「ヒッチハイク」したりしますか?ぎくしゃくしたアニメーションに気づきましたか?(開発者として、ある種のパフォーマンスの問題に敏感になり、他の問題には気付かないことに注意してください。それに応じて追加のテストを計画してください。)
  • スライディングウィンドウ5秒平均FPS であるFPSディスプレイをオンにします。計算と表示のオーバーヘッドはほとんどありません。
  • コードの各セクションの周りに単純な「ストップウォッチ」タイマーを使用して、フレームのさまざまな部分(vblank、preframe、update、collision、render、postframe)を表す一連の四角形(ROYGBIV色)であるプロファイルバーをオンにします。。必要なものを強調するために、1つの画面幅に相当するバーを60Hzのターゲットフレームを代表するように設定します。そのため、たとえば予算の50%(半分のバーのみ)か50%を超えているか(バーがラップして1.5バーになります)。一般的にフレームのほとんどを食べているものを伝えるのも非常に簡単です:赤=レンダリング、黄色=更新など...
  • 各機能の周りにコードのような「ストップウォッチ」を挿入する特別なインストルメントビルドビルドします。(これを行うと、大量のパフォーマンス、dcache、icacheヒットが発生する可能性があるため、間違いなく邪魔になります。ただし、適切なサンプリングプロファイラーやCPUの適切なサポートがない場合、これは許容可能なオプションです。機能上のデータの最小値を記録について/終了以降calltracesの再構築を入力してください。私たちは私たちを構築した場合)、私たちはずっとの模倣gprofの出力フォーマットさん。
  • 何よりも、サンプリングプロファイラを実行してください。VTuneとCodeAnalystはx86およびx64で使用できます。ここでデータを提供できるさまざまなシミュレーションまたはエミュレーション環境があります。

(幸福、無関心、イライラ、怒りの4枚の写真を撮り、フレームレートに基づいて内部ビルドの隅に適切な写真を表示したグラフィックプログラマーの過去1年間のGDCからの楽しいストーリーがあります。コンテンツ作成者は、すべてのオブジェクトと環境に対して複雑なシェーダーを有効にしないことをすぐに学びました。プログラマーを怒らせます。フィードバックの力を見守ってください。)

「プロファイルバー」を連続的にグラフ化するなどの楽しいこともできるため、スパイクパターン(「7フレームごとにフレームが失われている」)などを確認できます。

(そして多くの場合、やりがいの-私は通常何かを学ぶ)、それは魅力的ですが、私の経験では、命令または命令キャッシュやデータキャッシュのパフォーマンスの最適化番号に単一の機能/モジュールを書き換えるために、私たちは実際にんけれども、直接あなたの質問に答えるために必要とやってこれは、特に不快なパフォーマンスの問題が発生したときに、定期的に対処するパフォーマンスの問題の大部分が設計に起因することがあります。例えば:

  • プレーヤーの「攻撃」状態のアニメーションフレームをRAMにキャッシュするか、ディスクからリロードする必要がありますか?敵ごとにいかがですか?すべてを実行するためのRAMはありませんが、ディスクの負荷は高価です!5人または6人の敵が同時に現れた場合、ヒッチハイクを見ることができます!(さて、よろめき産卵はどうですか?)
  • すべてのパーティクルに対して単一のタイプの操作を実行していますか、それとも単一のパーティクルに対してすべての操作を実行していますか?(これはicache / dcacheのトレードオフであり、答えは必ずしも明確ではありません。)すべてのパーティクルを引き離し、位置(有名な「配列の構造」)を保存する方法と、すべてのパーティクルデータを1か所に保持する方法(構造体の配列」)。

大学レベルのコンピューターサイエンスコースで不快になるまで、あなたはそれを聞きますが、それは本当にデータ構造とアルゴリズムに関するものです。アルゴリズムとデータフローの設計に少し時間をかけることで、一般的な投資に見合うだけの価値を得ることができます。(Sony Developer Servicesフェローからのオブジェクト指向プログラミングの優れた落とし穴のスライド読んでください。ここでいくつかの洞察を得ることができます。)これは最適化のように「感じ」ません。現在のコードの実行を高速化するのではなく、ホワイトボードまたはUMLツールを使用したり、多くのプロトタイプを作成したりするのに費やした時間です。しかし、それは一般的にはるかに価値があります。

また、もう1つの有用なヒューリスティック:エンジンの「コア」に近い場合、最適化(たとえば、これらの行列の乗算をベクトル化する)に余分な労力と実験の価値があります。コアから遠いほど、プロファイリングツールのいずれかから別の指示がない限り、心配する必要は少なくなります。


6
  1. 適切なデータ構造とアルゴリズムを事前に使用します。
  2. プロファイルを作成し、ホットスポットの場所を正確に把握するまで、最適化を行わないでください。
  3. 賢いことを心配しないでください。コンパイラは、あなたが考えている小さなトリックをすべて実行します(「ああ!4倍する必要があります!左に2つシフトします!」)
  4. キャッシュミスに注意してください。

1
コンパイラに依存することは、特定の点でのみ賢明です。はい、それはあなたが考えないだろういくつかののぞき穴の最適化を行います(そしてアセンブリなしではできませんでした)、しかしあなたのアルゴリズムが何をするべきかについて無知であり、インテリジェントな最適化を行うことができません。また、アセンブリまたは組み込み関数に重要なコードを実装することでどれだけのサイクルを獲得できるか、驚くでしょう。コンパイラは、作られているほど賢くなく、どこでも明示的にemを指定しない限り(あなたが宗教的に 'restrict'を使用するなど)、あなたがすることを知りません。
カイ

1
繰り返しになりますが、ホットスポットのみを探すと、多くのサイクルを逃すことになります。これは、ボード全体に散りばめられたサイクルが見つからないためです(たとえば、スマートポインター....事実上、プログラム全体がホットスポットであるため)。
カイ

1
私はあなたの両方の点に同意しますが、そのほとんどを「正しいデータ構造とアルゴリズムを使用する」ことでまとめます。参照カウントされたスマートポインターをあちこちに渡しており、カウントのサイクルをブリーディングしている場合、間違ったデータ構造を間違いなく選択しています。
顕著な

5

ただし、「早すぎる悲観化」も忘れないでください。コードのすべての行をハードコアにする必要はありませんが、実際にゲームに取り組んでいることに気付く理由はあります。これはリアルタイムのパフォーマンスに影響を及ぼします。
誰もがホットスポットを測定して最適化するように指示しますが、その手法では、隠れた場所で失われたパフォーマンスは表示されません。たとえば、コード内のすべての「+」操作に必要な時間が2倍かかる場合、ホットスポットとして表示されないため、最適化または実現することはありませんが、それはあなたに多くのパフォーマンスを犠牲にするかもしれません。検出されずにそれらのサイクルのどれだけが滴り落ちるかに驚くでしょう。ですから、あなたが何をするのかに注意してください。
それとは別に、私は定期的にプロファイルを作成して、何があるか、フレームごとにどれだけの時間が残っているかを把握する傾向があります。私にとって、フレームレートの目標はどこにあるのかを直接教えてくれるので、フレームあたりの時間が最も論理的です。また、ピークがどこにあり、何が原因であるかを調べてみてください-スパイクのある高フレームレートよりも安定したフレームレートを好みます。


これは私にはとても間違っているようです。確かに、「+」は呼び出されるたびに2倍の時間がかかる場合がありますが、これは実際にはタイトなループでのみ重要です。タイトなループ内で、単一の「+」を変更すると、ループ外で「+」を変更するよりも桁違いに多くのことができます。ミリ秒を節約できるのに、なぜマイクロ秒の10分の1を考えるのですか?
-Wilduck

1
その場合、トリクルロスの背後にある考えを理解できません。「+」(例として)は、タイトなループだけでなく、フレームごとに数十万回と呼ばれます。ボード全体で多くのサイクルを失うたびにそれが数サイクル失われても、コールがコードベース/実行パス全体に均等に分散されるため、ホットスポットとして表示されることはありません。つまり、10分の1マイクロ秒ではなく、実際にはその10分の1マイクロ秒の数千倍であり、数ミリ秒になります。垂れ下がった果物(タイトループ)に行った後、この方法で2回以上ミリ秒を得ました。
カイ

それは滴るタップのようなものです。なぜその小さなドロップを保存することを心配するのですか?-「1秒間に1滴の割合で蛇口が垂れている場合、年間2700ガロンを無駄にすることが予想されます」。
カイ

ああ、私はoperator +がオーバーロードされたことを意味していたかどうかは明確ではなかったと思うので、コードのすべての '+'に影響します。実際、コードのすべての '+'を最適化する必要はありません。悪い例だと思う....「特に演算子のオーバーロードや他の難読化C ++構造によって隠されている場合、実装が想定よりも遅くなる可能性があるすべての場所で呼び出されるコア機能」の例として意味しました。
カイ

3

ゲームをリリースする準備ができたら(最終版またはベータ版)、または著しく遅い場合は、おそらくアプリをプロファイリングするのに最適なタイミングです。もちろん、いつでもプロファイラーを実行できます。しかし、はい、時期尚早な最適化はすべての悪の根源です。根拠のない最適化も。「最適化」を試みる前に、コードが遅いことを示すために実際のデータが必要です。プロファイラーがそれを行います。

プロファイラーについて知らない場合は、それを学んでください!ここだ優れたブログ記事プロファイラの有用性を実証するには。

ゲームコードの最適化のほとんどは、各フレームに必要なCPUサイクルを削減することです。これを行う1つの方法は、作成するすべてのルーチンを最適化し、可能な限り高速にすることです。ただし、CPUサイクルの90%がコードの10%に費やされるという一般的な言い方があります。これは、すべての最適化作業をこれらのボトルネックルーチンに向けると、すべてを均一に最適化する効果が10倍になることを意味します。では、これらのルーチンをどのように識別しますか?プロファイリングにより簡単になります。

さもなければ、小さなゲームが非効率的なアルゴリズムを持っているにもかかわらず200 FPSで実行されている場合、本当に最適化する理由がありますか?ターゲットマシンの仕様を十分に把握し、そのマシンでゲームが適切に動作することを確認する必要がありますが、それを超えると、おそらく間違いなくゲームのコーディングや洗練に費やすことができる無駄な時間になります。


ぶら下がっている果物は実際にコードの10%にある傾向があり、最終的にプロファイリングで簡単にキャッチされますが、このためにプロファイリングによって純粋に作業すると、多く呼び出されるが少ししか持っていないルーチンを逃すことになります少しずつ悪いコード-プロファイルには表示されませんが、呼び出しごとに多くのサイクルが発生します。それは本当に合計します。
カイ

@Kaj、良いプロファイラーは、悪いアルゴリズムの何百もの個々の実行のすべてを合計し、合計を表示します。次に、「10個の悪いメソッドがあり、すべてが1/10の頻度で呼び出された場合はどうなりますか?」と言います。これらの10の方法にすべての時間を費やしていると、低価格の果物がすべて失われ、そこからより大きな価値を得ることができます。
ジョンマクドナルド

2

プロファイリングを組み込むと便利です。積極的に最適化を行っていない場合でも、特定の時点でパフォーマンスを制限している要素を把握しておくとよいでしょう。多くのゲームには、ゲームループのさまざまな部分が各フレームを使用している時間を示す単純なグラフィカルチャート(通常は色付きのバー)を表示する、オーバーレイ可能なHUDがあります。

パフォーマンス分析と最適化を後期に遅すぎるようにするのは悪い考えです。すでにゲームをビルドしていて、CPU予算が200%を超えていて、最適化でそれを見つけられない場合は、失敗に終わります。

書くときは、グラフィックス、物理学などの予算を知る必要があります。パフォーマンスがどうなるかわからない場合、それはできません。また、パフォーマンスが何であるか、どの程度のスラックがあるかを知ることなく、それを推測することはできません。

そのため、初日からいくつかのパフォーマンス統計を組み込みます。

いつ物事に取り組むべきかについて-繰り返しますが、エンジンを半分リファクタリングしなければならないように、手遅れにしないでください。一方、明日アルゴリズムを完全に変更する可能性があると考えたり、実際のゲームデータを入れていない場合は、サイクルごとに絞り出すために最適化に縛られすぎないでください。

ぶら下がっている果物を取り除いて、定期的に大きなものに取り組むと、元気になります。


ゲーム内のプロファイラー(これは完全に同意します)に追加するには、ゲーム内のプロファイラーを拡張して(複数のフレームの)複数のバーを表示すると、ゲームの動作をスパイクと相関させ、平均的なキャプチャに表示されないボトルネックを見つけるのに役立ちますプロファイラーで。
カイ

2

Knuthの引用をその文脈で見て、彼が説明を続けると、プロファイラーのようなツールを使用して最適化する必要があると説明します。

非常に基本的なアーキテクチャが構築された後、アプリケーションのプロファイルとメモリのプロファイルを常に行う必要があります。

プロファイリングは、速度の向上に役立つだけでなく、バ​​グの発見にも役立ちます。プログラムが突然劇的に速度を変える場合、これは通常バグのためです。プロファイリングを行わないと、気付かない場合があります。

最適化の秘Theは、設計によってそれを行うことです。最後まで待たないでください。プログラムの設計が、本当に厄介な内部ループのトリックを使わずに、必要なパフォーマンスを提供することを確認してください。


1

私のプロジェクトでは、通常、非常に必要な最適化をベースエンジンに適用します。たとえば、私は常にSSE2と3DNow!を使用して、しっかりしたSIMD実装を実装したいと思っています。これにより、浮動小数点演算が目的の場所に合図されます。もう1つの優れた方法は、コードを作成するときに戻るのではなく、最適化を習慣にすることです。ほとんどの場合、これらの小さなプラクティスは、とにかくコーディングしていたのと同じくらい時間がかかります。機能をコーディングする前に、最も効率的な方法を調査してください。

結論としては、私の意見では、コードが既にひどい状態になった後、コードをより効率的にするためのハードルです。


0

最も簡単な方法は常識を使用することだと思います-何かが遅いように見える場合は、それを見てください。それがボトルネックであるかどうかを確認します。
プロファイラーを使用して、関数の処理速度と呼び出し頻度を確認します。
最適化することも、それを必要としないものを最適化しようとして時間を費やすことも絶対にありません。


0

コードの実行速度が遅い場合は、プロファイラーを実行して、正確に実行速度が遅くなる原因を確認してください。または、パフォーマンスの問題に気づき始める前にプロアクティブでプロファイラーを実行しておくこともできます。

ゲームが苦しみ始めるポイントまでフレームレートが低下したときに最適化する必要があります。最も可能性の高い犯人は、CPUが過剰に消費されていることです(100%)。


GPUはCPUと同じくらい可能性が高いと思います。実際、どの程度緊密に結合されているかにもよりますが、フレームの半分でCPUに強く縛られ、残りの半分でGPUに強く縛られる可能性があります。ダムプロファイリングは、どちらでも100%未満の使用率を示す場合さえあります。あなたのプロファイリングがそれを示すのに十分なきめ細かいものであることを確認してください(しかし、邪魔になるほどきめ細かいものではありません!)
JasonD

0

コードを最適化する必要があります...必要な頻度で。

過去に私がやったことは、プロファイリングをオンにしてゲームを継続的に実行することです(少なくとも画面上のフレームレートカウンターは常に)。ゲームが遅くなっている場合(最小スペックマシンのターゲットフレームレートなど)、プロファイラーをオンにして、ホットスポットが表示されるかどうかを確認します。

時にはコードではありません。私が過去に遭遇した問題の多くは、GPU指向のものでした(確かに、これはiPhoneにありました)。塗りつぶしの問題、描画呼び出しが多すぎる、十分なジオメトリをバッチ処理しない、非効率的なシェーダー...

困難な問題(つまり、経路探索、物理)の非効率的なアルゴリズム以外に、コード自体が原因である問題に遭遇することはほとんどありません。そして、これらの難しい問題は、アルゴリズムを正しくするために多くの労力を費やし、小さなことを心配しないことです。


0

私にとっては、準備の整った適切なデータモデルに従うことが最善です。そして、最適化-主要なステップの前に。大きな何かを実装する前に意味します。最適化のその他の理由は、リソースの制御を失い、アプリが多くのCPU負荷/ GPU負荷またはメモリを必要とし、理由がわからない場合です:)またはそれが多すぎます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.