実用的なパフォーマンスアプローチ

時期尚早な最適化は地獄にとって大切ですか? または、「その後修正」アプローチは、プログラマーを「専門家」から、すべてに軽spされる「男子生徒」に変えますか?



これらの質問には明確な答えはありませんが、この記事では、パフォーマンスに対する独自のアプローチについて説明します。 システムが適切な速度で動作することを保証するために、モジュール性、保守性、柔軟性などの他の要件に違反しないようにしています。



1.プログラマーの時間は最後のリソースです



大規模なプログラムを作成している場合、コードの一部は可能な限り最高の理論速度で動作しません。 言い換えるとすみません。 大規模なプログラムを作成している場合、コードのどの部分も理論上の最高速度で動作しません。 はい、コードのどの行も少し速く動作するように変更できることを理解する価値があると思います。



最大のパフォーマンスを達成するために、簡単なプログラムを作成することは必ずしも必要ではありません。 重要な場合、適切なパフォーマンスが必要です。 1年に1回呼び出されるコードを最適化するのに3週間費やす場合、これらの3週間をもっと意味のあることに費やすことができます。 それらを本当に重要なものに費やした場合、ゲームの描画速度を大幅に向上させることができます。



すべての新しい機能を追加し、すべてのバグを修正し、すべてのコードを最適化するのに十分な時間は常にありません。 したがって、目標は最小限の労力で最大限のパフォーマンスを実現することです。



2.シンプルさの力を過小評価しないでください



単純なソリューションは、複雑なソリューションよりも実装が簡単です。 しかし、これは氷山の一角にすぎません。 シンプルなソリューションの真の喜びは、時間とともにもたらされます。 シンプルなソリューションは、理解しやすく、デバッグしやすく、実装しやすく、移植しやすく、プロファイリングしやすく、最適化しやすく、並列化しやすく、交換しやすいです。 時間が経つにつれて、これらすべてのプラスが蓄積されます。



単純なソリューションを使用すると、複雑なソリューションよりも遅い場合でも時間を節約できます。 一般に、他の部分を最適化するために時間を節約するため、プログラムはより高速に実行されます。 本当に重要な部分。



複雑なソリューションを使用するのは1年だけで、これは実際に正当化されます。 たとえば、複雑なソリューションが単純なソリューション(またはファクター2など)よりもはるかに高速であり、システム内で本当に重要な場合(CPU時間のかなりの部分を消費する)。



もちろん、シンプルさは特定の人にしか見えません。 配列は単純だと思います。 PODデータ型はシンプルだと思います。 ブロブは簡単だと思います。 12レベルの継承を持つクラスは単純だとは思いません。 8つの異なるポリシーに基づくクラスは単純ではないと思います。 幾何学的代数は単純だとは思いません。



3.システムを設計する機会からあなたができるすべてを取ります



一部の人々は、「時期尚早の最適化」を行わないと、パフォーマンスにまったく注意を払わずにシステムを設計することを意味すると考えます。 最初に何かを盲目にしてから、「最適化」に取り組んでいるときに修正する必要があります。



このアプローチでは、私はまったく同意しません。 そして、私は生産性のためにパフォーマンスが好きだからではなく、純粋に実用的な関心からです。



システムを設計するとき、そのさまざまな部分がどのように組み合わされるか、それらに課される要件、および特定の機能が呼び出される頻度の完全な図を頭の中に持っています。 この段階では、システムの動作速度、およびデータ構造を整理して可能な限り迅速に動作させる方法についてもう少し考えるのに、それほど労力は必要ありません。



逆に、パフォーマンスに関係なくシステムを構築し、後で「修正」を行うと、事態はさらに複雑になる可能性があります。 基本的なデータ構造を再編成したり、マルチスレッドのサポートを導入したりする必要がある場合は、システム全体を最初から書き直すことがわかります。 システムのみが既に稼働中であり、公開されたAPIと他のシステムへのリンクに制限されます。 さらに、システムを使用するプロジェクトの内訳を許可することはできません。 そして、その時までにあなた(または他の誰か)がコードを書いてから数ヶ月が経過しているので、あなたはそれに入ったすべての考えを覚えて理解し始めなければなりません。 また、作成プロセス中に行われた小さな修正や修正はすべて失われる可能性があります。 そして、バグの更新されたバッグからデバッグを開始します。



したがって、「少ない労力で生産性を高める」という一般的な方針に従って、パフォーマンスの問題を最初から検討する価値があることがすぐにわかります。 後続の修正よりも少ない労力で済むからです。



慎重になる必要はありません。 パフォーマンスの改善は最初に導入する方が簡単ですが、それらがシステム全体に与える影響を正確に知ることはできません。 後で、プロファイリング後、より多くの労力が必要になりますが、何に焦点を当てるべきかがよりよくわかります。 一般に、人生の他の場所と同様に、バランスを維持することが重要です。



システムを設計するとき、単位時間あたりにコードのセクションが呼び出される頻度の大まかなスケッチを作成し、設計要件を策定します。



また、新しいシステムを作成するときに従うべきいくつかの推奨事項があります。



これまで、この「スタイル」で多くのシステムを作成してきましたが、これらの推奨事項に従うために努力する必要はありません。 そして、それらに従うことで、まともな基本的なパフォーマンスが得られるようにしました。 これらの推奨事項は、生産性を向上させる上で最も重要かつ容易に達成可能なポイントに関連しています。アルゴリズムの複雑さ、メモリアクセス、および同時実行性。 そして最も重要なことは、比較的少ない労力で生産性を大幅に向上させることです。



もちろん、すべての推奨事項に従うことが常に可能であるとは限りません。 たとえば、一部のアルゴリズムは実際にはO(n)時間以上かかります。 しかし、これらの推奨事項から逸脱した場合、パフォーマンスを低下させるかどうかを検討する必要があることは確かです。



4.トップダウンプロファイリングを使用してボトルネックを見つける



元のアーキテクチャがどれほど優れていても、最も予期しない場所でコードの速度が低下します。 人々はあなたのシステムを最もクレイジーな方法で使用し、想像もできなかったボトルネックを見つけます。 したがって、エラーコードにあります。 一部のエラーはシステムをクラッシュさせませんが、単にパフォーマンスに悪影響を及ぼします。 そして、これらはあなたが基本的に知ることができなかった間違いになります。



プログラムが実際に時間を浪費している場所を理解するために、トップダウンプロファイラーは非常に貴重なツールです。 コードのプロファイルされた部分を明示的に指定し、ライブデータをさまざまな方法で視覚化できる外部ツールにネットワーク経由で転送します。





(古い)BitSquidプロファイラーのスクリーンショット



トップダウンプロファイリングは、最適化の取り組みをどこに集中させるかを教えてくれます。 アニメーションに60%の時間を使用し、インターフェイスに0.5%の時間を使用しますか? アニメーションに携わる、それは動作しますが、インターフェイスとペニーはペニーの価値はありません。



上から下へプロファイリングすると、パフォーマンスの問題、つまり実際に時間が費やされる場所に到達するまで、プロファイルされたコードのセグメントを狭めたり狭めたりすることができます。



基本的な推奨事項を使用してすべてのシステムで良好な基本パフォーマンスを実現し、トップダウンプロファイリングを使用して掘り下げて、追加の最適化作業を必要とするシステムを見つけます。



5.ボトムアッププロファイリングを使用して、低レベルの最適化目標を見つける



一般に、明確に定義されたコードセグメントを使用したトップダウンプロファイリングは、ボトムアッププロファイリングよりも便利だと思います。



ただし、ボトムアッププロファイリングには使用シナリオがあります。 ホットスポット-プログラムのさまざまな部分から呼び出され、上から下へプロファイリングするときにスキップできる関数を見つけるのに適しています。 これらのホットスポットは、低レベルの命令ごとの最適化の適切なターゲットになります。 または、それらの存在は、何かが間違っていることを示しています。



たとえば、strcmp()関数がホットスポットとして表示される場合、プログラムの動作が非常に悪く、緊急にコーナーに配置してお菓子を奪う必要があります。



多くの場合、コードのホットスポットはlua_Vexecute()です。 驚くことではありません。 これは、Lua VMの主要な機能であり、Luaコードのほとんどを実行する大きなスイッチです。 しかし、これは、この関数の低レベルのプラットフォーム固有の最適化により、パフォーマンスが明確に向上することを示しています。



6.模擬テストを避けます。



コードをループで10,000回実行したり、ランタイムを測定したりするなど、多くの合成テストは行いません。



コードが変更後に高速化されるかどうかをすぐに理解できない部分を扱う場合、実際のゲームデータを使用した方がよいでしょう。 そうしないと、実際には見つからないデータの最適化を行っていないことを確信できません。



同じアニメーションを再生する1つのエンティティの500インスタンスをテストすることは、同じ場所をテストすることとは異なりますが、50の異なるユニットですべてが異なるアニメーションを持ちます。 データアクセスモデルは完全に異なります。 1つのケースのみを含む最適化は、別のケースでは重要ではありません。



7.最適化はガーデニングです



プログラマーはエンジンを最適化します。 脚本家はあらゆる種類の物を詰め込んだ。 だから、そうだったし、そうであろう。 そしてそれは良いことです。



最適化は、厳密に限られた時間内に発生する、ある種の孤立したプロセスではありません。 これは、開発、実装、開発というサイクル全体の一部です。 最適化とは、エンジン内にあるべきものについてプログラマとスクリプトライターが継続的に対話することです。



生産性の向上は、庭の世話をすることと同じです。植物の生活を良くする方法を見つけるために、すべてが雑草であるということを確認してください。



スクリプト作成者のタスクは、エンジンをドロップすることです。 そしてプログラマーの仕事はそれを元に戻すことであり、はるかに強力です。 そして、この対立の過程で、ゲームが最も明るく輝くポイントがあります。






翻訳。

オリジナルのパフォーマンスへの実用的なアプローチ



来てすべて! 優れた高速プログラムをたくさん書いてください!




All Articles