web-dev-qa-db-ja.com

開発の開始時または開発終了時に、ソフトウェアを最適化してパフォーマンスを向上させるのはいつですか。

私はジュニアのソフトウェア開発者であり、ソフトウェアを最適化してパフォーマンス(速度)を最適化するのに最適な時期はいつかと思っていました。

ソフトウェアが極端に大きく、管理するのが複雑ではない場合、最初に最適化するためにより多くの時間を費やす方が良いでしょうか、それともすべての機能を正しく実行するソフトウェアを開発してから、より良いパフォーマンスのために最適化に進むべきですか?

19
joepr

最も重要なことは、常に、そして永遠に読みやすさであるべきです。遅いが読みやすい場合は、修正できます。壊れているが読みやすい場合は、修正できます。それが読めない場合、私はこれが何をするはずだったのかを誰かに尋ねなければなりません。

可読性だけに焦点を合わせていた場合、コードがいかに優れているかは注目に値します。気になる理由が与えられるまで、私は一般的にパフォーマンスを無視します。これは、速度を気にしないという意味ではありません。私がやります。読みにくくすることで実際に解が速くなる問題はほとんどないことがわかりました。

このモードから抜け出すのは2つだけです。

  1. nがだれでも気に入るほど大きいときだけでさえ、完全に吹き飛ばされた big O の改善の機会を見たとき。
  2. 実際のパフォーマンスの問題を示すテストがある場合。数十年の経験があっても、数学よりもテストを信頼しています。そして、私は数学が得意です。

いずれの場合も、最速ではない可能性があるため、ソリューションを試してはいけないと考えさせて、 analysis paralysis を回避します。変更を加えると、変更が簡単な設計を使用する必要があるため、複数のソリューションを試してみると、コードは実際にメリットがあります。柔軟なコードベースは、本当に必要な場所で後で高速化できます。速度よりも柔軟に選択し、必要な速度を選択できます。

54
candied_orange

特定のレベルのパフォーマンスが必要な場合(機能以外の要件)、それは最初から設計目標にする必要があります。例えば。これは、どのテクノロジーが適切か、またはプログラム内のデータフローをどのように構造化するかに影響を与える可能性があります。

ただし、一般に、コードが記述される前に最適化することはできません。 最初に機能させ、次に正しく機能させ、最後に高速化します

ほとんどの機能を実装する前に最適化することの大きな問題の1つは、間違った場所で次善の設計決定に縛られてしまうことです。保守性とパフォーマンスの間にはしばしば(必ずというわけではありませんが)トレードオフがあります。プログラムのほとんどの部分は、パフォーマンスにはまったく関係ありません。一般的なプログラムには、最適化する価値のあるホットスポットがいくつかあります。したがって、パフォーマンスを必要としないすべての場所でパフォーマンスの保守性を犠牲にすることは、本当に悪いトレードです。

保守性を最適化することは、より良いアプローチです。保守性と明確な設計に 賢さ を費やすと、長期的にはクリティカルセクションを特定し、全体的な設計を損なうことなく安全に最適化することが容易になります。

27
amon

パフォーマンス(速度)を向上させるためにソフトウェアを最適化するのに最適な時期はいつでしょうか。

パフォーマンスはスピードと同じであるという概念を頭から取り除くことから始めます。 パフォーマンスは、ユーザーがパフォーマンスと信じているものです

アプリケーションをマウスクリックに対して2倍の速さで応答させ、10マイクロ秒から5マイクロ秒にすると、ユーザーは気にしません。アプリケーションをマウスのクリックに対して2倍の速さで応答させ、4千年から2千年にしても、ユーザーは気にしません。

アプリケーションを2倍の速さで作成し、マシンのすべてのメモリを使い果たしてクラッシュした場合、ユーザーはそれが2倍の速さであることを気にしません。

パフォーマンスは、特定のユーザーエクスペリエンスを実現するために、リソース消費について効果的なトレードオフを行うことの科学です。 ユーザーの時間は重要なリソースです、しかしそれは単に「より速い」だけでは決してありません。パフォーマンスの目標を達成するには、ほとんどの場合トレードオフが必要であり、時間とスペースのトレードオフ、またはその逆の場合がよくあります。

ソフトウェアが極端に大きく、管理が複雑ではないと仮定

それは恐ろしい仮定です。

ソフトウェアのサイズが大きく、管理が複雑でない場合は、ユーザーが気にする興味深い問題はおそらく解決されず、最適化も非常に簡単です。

最適化の最初により多くの時間を費やす方が良いですか、それともすべての機能を正しく実行するソフトウェアを開発してから、パフォーマンスを向上させるために最適化に進むべきですか?

あなたはそこに空白のページで座っており、あなたはvoid main() {}と書きます。最適化を開始しますか?最適化するものは何もありません!正しい順序は次のとおりです。

  • コンパイルさせる
  • 正しくする
  • エレガントにする
  • 速くする

他の順序でそれを行おうとすると、混乱する間違ったコードになってしまい、今では間違った答えを非常に迅速に生成し、変更に抵抗するプログラムを手に入れました。

しかし、足りないステップがあります。 realの正しい順序は次のとおりです。

  • 顧客や経営陣と協力して、現実的で測定可能なパフォーマンスメトリックと目標を設定します。顧客が重視するメトリックは速度だけではないことに注意してください。
  • 目標に対してプロジェクトの現在の状態を追跡できるテストハーネスを実装する
  • コンパイルさせる
  • テストを実行します。目標を達成できなくなった場合は、早い段階で悪い道を進んでいた可能性があることを認識してください。 科学を使用。修正できる悪いアルゴリズムを導入しましたか、それとも根本的に何か間違っていますか?それが根本的に間違っている場合は、最初からやり直してください。修正できる場合は、バグを入力して後で戻ってください。
  • 正しくする
  • テストを再実行してください...
  • エレガントにする
  • テストを再実行してください...
  • 目標を達成していますか?はいの場合、ビーチに行きます。そうでない場合は、高速にしてください十分
16
Eric Lippert

原則として、後でパフォーマンスを最適化するのが最適ですが、大きな負荷やデータが追加されると遅くなるソフトウェアが開発者にもたらされたと開発者が気づいたときに、多くのプロジェクトが失敗するのを見てきました。

したがって、私の考えでは、ミドルグラウンドアプローチが最適です。過度に強調しないでください。ただし、パフォーマンスはまったく無視しないでください。

私が何度も見た例を挙げます。 ORMライブラリがある場合、1つ以上の注文を持つことができるユーザーエンティティがあります。ユーザーのすべての注文をループして、ユーザーがストアで費やした金額を調べましょう-素朴なアプローチ:

User user = getUser();
int totalAmount;
for (Order o : user.getOrders()) {
  totalAmount += o.getTotalAmount();
} 

開発者が影響を考慮せずに同様のことを書くのを見てきました。最初にユーザーを取得します。これは、ユーザーテーブルの1つのSQLクエリになると期待できます(ただし、はるかに多くのことを伴う場合があります)。次に、注文をループします。 、製品情報など-これはすべて、注文ごとに単一の整数を取得するためです!

ここでのSQLクエリの量は驚くかもしれません。もちろん、エンティティの構造に依存します。

ここで、正しいアプローチは、ORMによって提供されるクエリ言語で記述された別のクエリを介してデータベースから合計を取得する別の関数を追加することである可能性が高く、これを行うことを推奨します初めて、これを後で延期しないこと。そうした場合、おそらく処理すべき問題がさらに多くなり、どこから始めればよいかわからないためです。

3
Vetle

システム全体のパフォーマンスは、システムコンポーネント全体の複雑な相互作用の結果です。それは非線形システムです。したがって、パフォーマンスはコンポーネントの個々のパフォーマンスだけでなく、それらの間のbottlenecksによってゲートされます。

システムのすべてのコンポーネントがまだ構築されていない場合は、明らかにボトルネックをテストできないため、初期の段階で実際に十分にテストすることはできません。一方、システムが構築された後は、必要なパフォーマンスを得るために必要な変更を簡単に加えることができない場合があります。つまり、これは正真正銘の Catch-22 です。

問題をさらに難しくするために、本番環境のような環境に切り替えると、パフォーマンスプロファイルが大幅に変化する可能性があります。

それで、あなたは何をしますか?まあ、いくつかのこと。

  1. 実用的です。初期の段階では、パフォーマンスの「ベストプラクティス」であるプラットフォーム機能の使用を選択できます。たとえば、接続プーリング、非同期トランザクションを利用し、ステートフル性を回避します。これは、異なるワーカーが共有データへのアクセスを求めて競合しているマルチスレッドアプリケーションの死を招く可能性があります。通常、これらのパターンのパフォーマンスをテストするのではなく、何がうまく機能するかを経験から知るだけです。

  2. 繰り返します。システムが比較的新しいときにベースラインパフォーマンス測定を行い、時々再テストして、新しく導入されたコードがパフォーマンスを大幅に低下させていないことを確認します。

  3. 早期に過剰最適化しないでください。何が重要で何が問題にならないかは決してわかりません。たとえば、プログラムが常にI/Oを待機している場合、超高速文字列解析アルゴリズムは役に立たない可能性があります。

  4. 特にWebアプリケーションでは、パフォーマンスではなくスケーラビリティに重点を置くことができます。アプリケーションがスケールアウトできる場合、十分に速くなるまでファームにノードを追加し続けることができるため、パフォーマンスはほとんど問題になりません。

  5. データベースには特に注意が必要です。トランザクションの整合性の制約により、データベースはシステムのすべての部分を支配するボトルネックになる傾向があります。高性能システムが必要な場合は、データベース側で作業し、クエリプランを確認し、一般的な操作を可能な限り効率化するテーブルとインデックスの構造を開発している有能な人材がいることを確認してください。

これらのアクティビティのほとんどは、プロジェクトの開始または終了ではありませんが、継続的に参加する必要があります

3
John Wu

画像処理やレイトレーシングなど、パフォーマンスが非常に重要な領域での作業に偏りがあるかもしれませんが、それでも「できるだけ遅く」最適化するように言います。要件がどれほどパフォーマンスクリティカルであるかに関係なく、測定した後は、事前よりも常に多くの情報と明快さがわかります。これは、最も効果的な最適化でさえ、通常、そのような知識を得た後に適用されることを意味します。

特異なケース

しかし時々「できるだけ遅く」いくつかの特異なケースでは、まだかなり早い段階でいまいましい。たとえば、オフラインレンダラーについて話している場合、パフォーマンスを達成するために使用するデータ構造と手法は、実際にはユーザーエンドデザインに浸透します。これは不快に聞こえるかもしれませんが、フィールドは最先端で非常にパフォーマンスが非常に高いため、ユーザーは特定のレイトレーサーに適用可能な最適化手法(例:放射キャッシングまたはフォトンマッピング)に固有のユーザーエンドコントロールを受け入れます。画像がレンダリングされるまで何時間もかかるほか、レンダリング専用のマシンを備えたレンダーファームをレンタルまたは所有するために莫大なお金を使い果たしている人もいます。競争力のあるオフラインレンダラーがレンダリングに費やされる時間の重要な削減を提供できる場合、これらのユーザーの時間と費用は大幅に削減されます。これは、時間の5%削減が実際にユーザーを興奮させる一種の領域です。

このような特殊なケースでは、ユーザーエンドデザインを含むデザイン全体が使用するデータ構造とアルゴリズムを中心に展開するため、1つのレンダリング手法を無意味に選択して後で最適化することはできません。ここでは、個人として、そしてあなたの特定の長所と短所が、競争力のあるソリューションを提供することに重きを置いているため、他の人にとってうまくいったことだけでうまくいくとは限りません。 Arnoldの背後にある主な開発者の考え方と感性は、非常に異なるアプローチを使用したVRayで作業している開発者とは異なります。彼らは必ずしも場所/技術を入れ替えて最高の仕事をすることができるわけではありません(彼らは両方とも産業のリーダーであるとしても)。実際に売れるような競争力のある製品を出荷したい場合は、実験とプロトタイプとベンチマークのようなものを用意し、最先端の技術の無限の配列を考えると、特に得意なことを見つける必要があります。したがって、この特異なケースでは、パフォーマンスの懸念は、開発を開始する前であっても、おそらく最も重要な懸念として、最前線に移動します。

それでも、最適化の違反ではありません "できるだけ遅く"、それだけです "できるだけ遅く"はかなり早いですこれらの極端で特異な場合。そのような初期のパフォーマンスの懸念が必要である場合、それが必要である場合でも、それが必要であるかどうかを把握することは、おそらく開発者にとって主な課題です。すべてを最適化したい素朴な開発者(そして残念ながらなんとかして仕事を続けられたベテランでさえも)が不足していないため、最適化しないものは、開発者のキャリアにおいて学び、学び続ける上で最も価値のあるものの1つかもしれません。彼らの反生産性にもかかわらず)。

できるだけ遅く

おそらく最も難しいのは、それが何を意味するのかを理解しようとすることです。私はまだ学び続けており、プログラミングを行って30年近くになります。しかし、特に今から30年間、私はそれがそれほど難しくないことに気づき始めています。実装よりも設計に重点を置いた場合、それはロケット科学ではありません。設計を変更せずに、後で適切な最適化のための余地を残しておくほど、最適化できます。そして、私にそのような余裕のあるデザインを探すことで、ますます生産性が向上しました。

後で最適化するための呼吸空間を提供する設計

これらのタイプのデザインは、「常識」を適用することができれば、ほとんどの場合、実際にはそれほど難しくありません。個人的な話として、趣味としてビジュアルアートに興味があります(アーティストが自分のニーズを理解して自分の言語を話すためにソフトウェアをプログラムするのはある程度役立つと思います)。自分の作品を落書きして共有したり、他のアーティストと交流したりする簡単な方法として、オンラインで.

特に、私のお気に入りのサイトとアプレットには、パフォーマンスの問題があり、つまらないブラシサイズではクロールが遅くなりますが、非常に素晴らしいコミュニティがありました。パフォーマンスの問題を回避するために、1ピクセルまたは2ピクセルの小さなブラシを使用して、次のように筆記体を書きました。

enter image description here

その間、私はソフトウェアの作者にパフォーマンスを改善するための提案を与え続け、彼は私の提案がメモリの最適化やアルゴリズムなどについて話す特に技術的な性質のものであることに気づきました。それで、彼は私がプログラマーであるかどうか実際に尋ねました、そして私ははいと言って、そして彼はソースコードに取り組むように私を招待しました。

だから私はソースコードを見て、それを実行し、プロファイリングしました、そして私の恐怖に彼はIPixelのような「抽象的なピクセルインターフェース」の概念に基づいてソフトウェアを設計しました。動的な割り当てを備えたすべてのトップホットスポットと、すべての画像のすべてのピクセルのディスパッチ。しかし、ソフトウェアの設計全体を再検討せずにそれを最適化する実用的な方法はありませんでした。なぜなら、私たちの抽象化が単一の抽象ピクセルの細かいレベルで機能しているとき、設計はマイクロ最適化の最も些細なことをはるかに超えない隅に追い込まれたためです。そしてすべてはこの抽象的なピクセルに依存しています。それで、大きなブラシやリアルタイムフィルターなどを処理するためにソフトウェアを最適化するという考えをあきらめ、1ピクセルまたは2ピクセルのブラシを使った落書きに戻りました。

それは「常識」の違反だと思いますが、明らかに開発者にとっては常識ではありませんでした。しかし、ピクセルやパーティクル、あるいは巨大な軍隊シミュレーションの小さなユニットのように、最も基本的なユースケースでさえも何百万ものインスタンス化が行われるような細かいレベルで抽象化しないようなものです。 IImage(より大きな集約レベルで必要なすべての画像/ピクセル形式を処理できます)またはIParticleSystemからIPixelまたはIParticleを優先し、次にそのようなインターフェースの背後に、最も基本的で書き込みが簡単で理解しやすい実装を組み込むことができ、ソフトウェア全体の設計を再検討することなく、後で最適化する必要があるすべての余地を確保できます。

そして、私が最近目にする目標はそれです。上記のオフラインレンダラーのような特異なケースを除外して、可能な限り多くの後知恵情報(測定を含む)を使用して、可能な限り遅く最適化するのに十分な余裕を持って設計し、必要な最適化をできるだけ遅く適用します。

もちろん、一般的なユーザーエンドのケースでは簡単ではないサイズに簡単に到達する入力に対して、2次複雑度アルゴリズムを使用することから始めることを必ずしも提案しているわけではありません。とにかく誰がそれをしますか?しかし、実装が後で簡単に入れ替えることができれば、それはそれほど大きな問題だとは思いません。デザインを再検討する必要がない場合でも、それは重大な間違いではありません。

1
Dragon Energy

性能も保守性もないコードを書くのが最も簡単です。ポルフォルマントなコードを書くのは難しいです。メンテナンス可能なコードを書くのはまだ難しいです。また、保守性とパフォーマンスの両方を兼ね備えたコードを作成することは最も困難です。

ただし、実行可能なコードを保守可能にするよりも、保守可能なコードを実行可能にする方が簡単です。

さて、明らかに、それはあなたが作っているシステムのタイプに依存します。いくつかのシステムはパフォーマンスが非常に重要で、最初から計画されている必要があります。上記で答えたエリック・リッパートのような非常に才能のある人々にとって、これらのシステムは一般的かもしれません。しかし、私たちのほとんどにとって、それらは私たちが構築するシステムの少数派です。

ただし、最新のハードウェアの状態を考えると、システムの大部分では、最初から最適化に特別な注意を払う必要はありませんが、 パフォーマンスの低下を回避するで通常は十分です。つまり、単にselect count(*) from tableをクエリするのではなく、テーブルのすべてのレコードを元に戻してカウントを取得するなど、明らかに愚かなことをしないでください。間違いを避け、使用しているツールを理解するよう努力してください。

次に、まずコードを保守可能にすることに焦点を当てます。これにより、つまり:

  1. 懸念事項をできる限り厳密に分離します(たとえば、データアクセスとビジネスロジックを混在させないでください)。
  2. 可能な場合は具象型ではなく抽象型を参照する
  3. コードをテスト可能にする

統計がそれが必要であると示したとき、保守可能なコードは最適化するのがはるかに簡単です。

次に、コードに[〜#〜] lots [〜#〜]自動テストがあることを確認します。これにはいくつかの利点があります。バグが少ないほど、最適化に時間がかかります必要な場合。また、最適化を行うと、実装内のバグをはるかに早く見つけることができるため、最適なソリューションをより迅速に反復して見つけることができます。

自動化された展開スクリプトとスクリプト化されたインフラストラクチャは、パフォーマンスのチューニングにも非常に役立ちます。他の利点は言うまでもありません。

したがって、いつものように、例外があります(特定するには経験が必要です)が、一般的に、私のアドバイスは次のとおりです。まず、ツールを学習し、パフォーマンスのボトルネックをコーディングしないようにします。次に、コードが保守可能であることを確認します。第三に、自動テスト。 4番目に、完全に自動化された展開。これらのことを行って初めて、最適化について心配する必要があります。

1
TheCatWhisperer

私はジュニアソフトウェア開発者で、パフォーマンス(速度)を向上させるためにソフトウェアを最適化するのに最適な時期はいつかと思っていました。

2つの非常に異なる極値があることを理解してください。

最初の極端は、設計の大部分に影響を与えるものです。たとえば、作業をいくつのプロセスやスレッドに分割するか、ピースがどのように通信するか(TCP/IPソケット?直接関数呼び出し?)、高度なJITを実装するかどうかなどです。または「一度に1つのオペコード」インタープリター、またはSIMDに対応できるようにデータ構造を計画するかどうか...これらは実装に強い影響を及ぼし、後から改造するのが非常に困難/高価になる傾向があります。

もう1つの極端な方法は、マイクロ最適化です-あらゆる場所で小さな小さな微調整を行います。これらのことは実装にほとんど影響を与えない傾向があり(そして多くの場合、とにかくコンパイラーによって最もよく行われます)、好きなときにいつでもこれらの最適化を行うことは簡単です。

これらの両極端の間には巨大な灰色の領域があります。

それが実際に降りかかるのは、「利益がコストを正当化するか」という質問に答えるために使用される経験/教育された推測です。極端に、またはその近くの最適化では、多くの場合間違っていると推測すると、すべての作業が破棄され、ゼロまたはプロジェクトの失敗(不必要に複雑すぎる設計に費やされる時間が多すぎる)から再開することを意味します。もう一方の極端な場所またはその近くでは、測定(プロファイリングなど)を使用して問題があることを証明できるまで、それをそのままにしておく方がはるかに賢明です。

残念ながら、私たちは、最適化には(ほとんど無関係)なことが「ごくわずか」な極端にしか含まれないと考える人が多すぎる世界に住んでいます。

1
Brendan

そのパフォーマンスがアプリケーションにとって何を意味するかによって異なります。そして、アプリケーションが機能的に完了する前にパフォーマンスを最適化することさえ可能かどうかについて。

ほとんどの場合、何もすることがなくなるまで心配する必要はありませんが、特定のレベルのパフォーマンスがアプリケーションの成功に不可欠である可能性があります。それが事実であり、それが容易ではない可能性があると思われる場合は、「すぐに失敗する」ためにパフォーマンスの調査を開始する必要があります。

プロジェクトの重要な原則は、最初にハードパーツに焦点を当てることです。そうすれば、それができないことが判明した場合でも、早い段階で気づき、まったく違うことを試す時間があるか、プロジェクトに費やしすぎる前にプロジェクトがキャンセルされる可能性があります。

0
Martin Maat

パフォーマンスは速度以上のものであることをお勧めします。これには、規模(数百から数千の同時ユーザー)が含まれます。確かに、アプリケーションが本番環境の負荷を取得したときに、アプリケーションが停止しないようにする必要があります。パフォーマンスには、アプリケーションが消費するリソース(メモリなど)の量が含まれます。

性能も使いやすさです。一部のユーザーは、2回のキーストロークで1秒でタスクを実行するよりも、1回のキーストロークで10秒でタスクを実行したいと考えています。そのようなことについては、設計リーダーに尋ねてください。このようなものをユーザーに早期に提供したくありません。真空の中で彼らはXと言うかもしれませんが、彼らが機能的なプレリリースで作業していると、彼らはYと言うかもしれません。

個々の最高速度は、データベース接続などのリソースを保持することです。ただし、スケールの場合は、接続をできるだけ遅く取得し、できるだけ早く解放する必要があります。 3つのものを取得するためのデータベースへの1回の移動は、データベースへの3回の個別の移動よりも高速です。

セッション中に変化しない情報を求めて旅行していますか?もしそうなら、セッションの開始時にそれを取得し、それはメモリです。

コレクションタイプを選択するときは、機能、速度、サイズを考慮してください。

コレクションにアイテムを保持する必要がありますか?一般的な問題は、ファイルからすべての行をリストに読み込み、リストを一度に1行ずつ処理することです。ファイルを1行ずつ読み取り、リストをスキップする方がはるかに効率的です。

1回ループして3つのことを実行できるときに、3回ループしていますか?.

コールバックで別のスレッドで処理する必要がある可能性がある場所はありますか?その場合、差し迫った設計のニーズに干渉しない場合は、その可能性のあるニーズを考慮してコードをパッケージ化します。

多くのパフォーマンスもクリーンなコードです。

時期尚早な最適化があり、前もって常識的なことを行っているだけで、それほど時間はかかりません。

データベースでは、時期尚早の最適化が見られます。速度の問題が発生する前に速度を非正規化します。私が得る議論は、後でテーブルを変更する場合、すべてを変更する必要があるということです。多くの場合、データをそのまま表示するビューを作成でき、後で非正規化テーブルと交換する必要がある場合があります。

0
paparazzo

まず [〜#〜] mvp [〜#〜] を顧客に渡します。次に、統計プロファイリングを実行して、何が遅いかを確認します。

0
guettli