web-dev-qa-db-ja.com

時期尚早な最適化は本当にすべての悪の根源ですか?

私の同僚は本日、ThreadLocalFormatというクラスをコミットしました。これは基本的にJava Formatクラスのインスタンスをスレッドローカルに移動しました。これは、クラスがスレッドセーフでなく、「比較的高価」ではないためです。作成しました。簡単なテストを書いて、1秒間に200,000のインスタンスを作成できると計算し、その数を作成するように求められました。それには「その数の近くにはありません」と答えました。彼は優れたプログラマーであり、チームの全員が非常に熟練しているので、結果のコードを理解するのに問題はありませんが、それは明らかに、本当に必要のない場所を最適化するケースでした。彼は私のリクエストでコードをバックアウトしました。どう思いますか?これは「時期尚早の最適化」のケースであり、どれほど悪いですか本当か?

224
Craig Day

完全な引用を覚えておくことが重要です:

小さな効率については忘れてください。たとえば、97%の時間です。時期尚早な最適化がすべての悪の根源です。それでも、その重要な3%で機会を逃してはなりません。

つまり、測定されたパフォーマンスの問題がない場合、考えるのでパフォーマンスが向上するため、最適化すべきではありません。明確な最適化(タイトループ内で文字列の連結を行わないなど)がありますが、それが測定できるようになるまで、明らかな最適化ではないものは避けてください。

「時期尚早の最適化」の最大の問題は、予期しないバグが発生し、時間を大幅に浪費する可能性があることです。

333
Scott Dorman

時期尚早micro最適化はすべての悪の根源です。なぜなら、マイクロ最適化はコンテキストを除外するからです。彼らは彼らが期待されるように振る舞うことはほとんどありません。

重要な順に、初期の最適化にはどのようなものがありますか。

  • アーキテクチャの最適化(アプリケーション構造、コンポーネント化および階層化の方法)
  • データフローの最適化(アプリケーションの内部と外部)

いくつかの開発中期の最適化:

  • データ構造。パフォーマンスが向上するか、必要に応じてオーバーヘッドが小さくなる新しいデータ構造を導入します
  • アルゴリズム(quicksort3とheapsortの間で決定を始める良い機会です;-))

開発サイクルの一部の最適化

  • コードホットポット(タイトなループ、最適化が必要)を見つける
  • コードの計算部分のプロファイリングベースの最適化
  • マイクロ最適化は、アプリケーションのコンテキストで行われ、その影響を正しく測定できるようになったため、今すぐ行うことができます。

初期のすべての最適化が悪であるとは限りませんマイクロ最適化は、開発ライフサイクルの間違った時期に行われると悪ですアーキテクチャに悪影響を与える可能性、初期の生産性に悪影響を及ぼす可能性があるため、パフォーマンスに関係なく、あるいは環境条件が異なるため、開発の終わりに有害な影響を及ぼします。

パフォーマンスが気になる場合(そして常に気にする必要がある場合)は、常にbigと考えてください。パフォーマンスは全体像であり、次のようなものではありません:intまたはlong?. Bottom Upの代わりにパフォーマンスを使用する場合は、Top Downを選択します。

112
Pop Catalin

最適化最初の測定なしはほぼ常に時期尚早です。

これはこの場合に当てはまり、一般的な場合にも当てはまると思います。

54
Jeff Atwood

次のような場合、最適化は「悪」です。

  • 明確でないコード
  • 大幅に多くのコード
  • 安全性の低いコード
  • 無駄なプログラマー時間

あなたの場合、プログラマーの少しの時間がすでに費やされているようで、コードはそれほど複雑ではなく(チームの全員が理解できるとあなたのコメントから推測)、コードはもう少し将来の証拠です(私があなたの説明を理解していれば、今はスレッドセーフです。少し悪のように聞こえます。 :)

44
John Mulder

この質問が5年前のものであることに驚いていますが、Knuthのコメントを2、3文以上投稿した人はいません。有名な引用を取り巻くいくつかのパラグラフはそれをかなりよく説明しています。引用されている論文は「 go to Statementsを使用した構造化プログラミング 」と呼ばれ、40年近くの歴史がありますが、論争とソフトウェアのムーブメントに関するものであり、どちらも存在しません。多くの人々が聞いたことのないプログラミング言語であり、驚くほど大量にそれがまだ当てはまります。

これはより大きな引用です(PDFの8ページ、元の268ページから):

例2から例2aへの速度の改善は約12%に過ぎず、多くの人はそれを取るに足らないと言っています。今日のソフトウェアエンジニアの多くが共有している従来の知恵は、小規模の効率を無視することを求めています。しかし、これは、「最適化された」プログラムをデバッグまたは維持できないペニーワイズでポンドバカなプログラマーによって行われている虐待に対する過剰反応であると私は信じています。確立されたエンジニアリング分野では、簡単に得られる12%の改善が限界と見なされることはありません。また、ソフトウェアエンジニアリングにも同じ視点が広まるはずです。もちろん、私はワンショットの仕事でそのような最適化を行うことを気にしませんが、質の高いプログラムの準備の問題であるとき、私はそのような効率を否定するツールに自分を制限したくありません。

効率の悪さが虐待につながることは間違いありません。プログラマーは、プログラムの重要ではない部分の速度について考えたり心配したりすることに膨大な時間を費やします。デバッグや保守を検討する場合、これらの効率化の試みは実際には強力な悪影響を及ぼします。私たちはすべきである小さな効率を忘れて、時間の約97%を言います:時期尚早な最適化はすべての悪の根です。

しかし、その重要な3%で機会を逃してはなりません。優れたプログラマーは、そのような推論によって自己満足に陥ることはなく、重要なコードを注意深く検討するのが賢明です。しかし、そのコードが識別された後にのみ。多くの場合、測定ツールを使用してきたプログラマーの普遍的な経験は、直感的な推測が失敗するため、プログラムのどの部分が本当に重要であるかについて事前に判断することは誤りです。

前のページからのもう一つの良いビット:

私のプログラミングスタイルはもちろん、過去10年間に時代の傾向に応じて変更されています(たとえば、私はもうそれほどトリッキーではなく、使用する回数も少なくなっています)が、私のスタイルの大きな変更は当然のことですこの内部ループ現象に。次に、重要な内部ループのすべての操作を非常に黄昏の目で見て、プログラムとデータ構造を変更して(例1から例2への変更のように)、一部の操作を削除できるようにします。このアプローチの理由は次のとおりです。a)内側のループが短いため、時間がかかりません。 b)見返りは現実的です。 c)次に、プログラムの他の部分では効率が低下することを許容できるため、プログラムがより読みやすく、より簡単に記述およびデバッグできます。

39
Michael Shaw

この引用が明らかに悪いコードや正当化されたコードを正当化するために使用されたことがよくありますが、そのパフォーマンスは測定されていませんが、コードサイズを大きくしたり、読みやすさを損なうことなく、かなり簡単に高速化できるでしょう。

一般に、初期のマイクロ最適化は悪い考えかもしれません。ただし、マクロ最適化(O(N ^ 2)の代わりにO(log N)アルゴリズムを選択するようなもの)は多くの場合価値があり、O(N ^ 2)アルゴリズムを記述して、次に、O(log N)アプローチを採用して、完全に破棄します。

多分という単語に注意してください。O(N ^ 2)アルゴリズムがシンプルで簡単に記述できる場合、遅すぎることが判明しても、あまり罪悪感を感じることなく後で破棄できます。ただし、両方のアルゴリズムが同様に複雑である場合、または予想されるワークロードが非常に大きいため、より高速なアルゴリズムが必要であることがすでにわかっている場合、早期に最適化することは、長期的には総ワークロードを削減する適切なエンジニアリング決定です。

したがって、一般的に、適切なアプローチは、コードを書き始める前に選択肢を見つけ、状況に応じて最適なアルゴリズムを意識的に選択することだと思います。最も重要なのは、「時期尚早な最適化はすべての悪の根源である」というフレーズは、無知の言い訳にはなりません。キャリア開発者は、一般的な運用コストの概算を知っている必要があります。たとえば、彼らは知っておくべきです

  • その文字列は数字よりもコストがかかる
  • 動的言語は静的型付け言語よりもはるかに遅い
  • リンクリストに対する配列/ベクトルリストの利点、およびその逆
  • ハッシュテーブルをいつ使用するか、ソート済みマップをいつ使用するか、およびヒープをいつ使用するか
  • (モバイルデバイスで動作する場合)「ダブル」と「int」はデスクトップで同様のパフォーマンスを発揮しますが(FPはさらに高速かもしれません)、FPUのないローエンドのモバイルデバイスでは「ダブル」が100倍遅くなる場合があります。
  • インターネット経由でのデータ転送はHDDアクセスよりも遅いこと、HDDはRAMよりもはるかに遅いこと、RAMはL1キャッシュおよびレジスターよりもはるかに遅いこと、およびインターネット操作は無期限にブロックされる可能性があり、いつでも失敗する可能性があります)。

また、開発者はデータ構造とアルゴリズムのツールボックスに精通している必要があります。そうすることで、開発者は仕事に適したツールを簡単に使用できます。

豊富な知識と個人的なツールボックスがあると、ほとんど苦労せずに最適化できます。不必要かもしれない最適化に多大な労力を費やすis evil(そして私はそのトラップに何度も陥ることを認めます)。しかし、最適化が配列の代わりにセット/ハッシュテーブルを選択する、または文字列[]の代わりにdouble []に​​数値のリストを格納するのと同じくらい簡単な場合、なぜでしょうか?私はここではKnuthに同意しないかもしれませんが、わかりませんが、彼は低レベルの最適化について話していたのに対して、私は高レベルの最適化について話していると思います。

覚えておいてください。その引用は元々1974年からのものです。1974年のコンピューターは遅く、計算能力が高かったため、一部の開発者は1行ずつ最適化しすぎる傾向がありました。それがKnuthが反対していたことだと思います。彼は「パフォーマンスについてはまったく心配しないでください」とは言っていませんでした。なぜなら、1974年にはそれはおかしな話になるからです。 Knuthはhowを説明して最適化しました。つまり、ボトルネックにのみ焦点を当てる必要があり、その前に測定を実行してボトルネックを見つける必要があります。

測定するプログラムを作成するまではボトルネックを見つけることができないことに注意してください。つまり、測定するものが存在する前に、パフォーマンスの決定mustを行う必要があります。これらの決定を間違えた場合、変更を変更することが難しい場合があります。このため、ハードデータが利用できない場合に合理的な決定を行うことができるように、コストの概算を把握しておくことをお勧めします。

どのくらい早く最適化するか、そしてパフォーマンスについてどれだけ心配するかは、ジョブによって異なります。数回しか実行しないスクリプトを作成する場合、パフォーマンスをまったく気にすることは、通常、時間の無駄です。しかし、MicrosoftまたはOracleで働いており、-数千人の他の開発者が数千の異なる方法で使用するライブラリに取り組んでいる場合、それから地獄を最適化するのにお金がかかるかもしれません。多様なユースケースすべてを効率的にカバーできます。それでも、パフォーマンスの必要性は常に、読みやすさ、保守性、優雅さ、拡張性などの必要性とのバランスをとる必要があります。

21
Qwertie

個人的には、 前のスレッド で説明されているように、パフォーマンスの問題が発生することがわかっている状況では、早期の最適化が悪いとは思いません。たとえば、私は何千万ものエンティティを定期的に処理するサーフェスモデリングおよび分析ソフトウェアを作成しています。設計段階で最適なパフォーマンスを計画することは、脆弱な設計の後期の最適化よりもはるかに優れています。

考慮すべきもう1つのことは、アプリケーションが将来どのようにスケーリングするかです。コードの寿命が長いと考える場合は、設計段階でパフォーマンスを最適化することもお勧めします。

私の経験では、最適化の遅れは、高額でわずかな報酬を提供します。アルゴリズムの選択と調整による設計段階での最適化は、はるかに優れています。プロファイラーによっては、コードがどのように機能するかを理解することは、高性能コードを取得するための優れた方法ではありません。事前に知っておく必要があります。

14
SmacL

実際、時期尚早の非最適化がすべての悪の原因であることが多いことを学びました。

人々がソフトウェアを書くとき、それは最初に不安定さ、制限された機能、悪いユーザビリティと悪いパフォーマンスのような問題を抱えています。これらすべては通常、ソフトウェアが成熟すると修正されます。

パフォーマンスを除く、これらすべて。誰もパフォーマンスを気にしないようです。理由は単純です。ソフトウェアがクラッシュした場合、誰かがバグを修正し、それだけです。機能が欠落している場合は、誰かが実装して実行します。ソフトウェアのパフォーマンスが悪い場合、多くの場合、マイクロ最適化が欠落しているためではありませんが、設計が悪いため、ソフトウェアの設計には誰も触れません。これまで。

Bochsを見てください。地獄のように遅いです。速くなるのでしょうか?たぶん、しかし数パーセントの範囲でのみです。 VMWareやVBoxなどの仮想化ソフトウェアやQEMUに匹敵するパフォーマンスは得られません。設計上遅いからです!

ソフトウェアの問題が遅いことである場合、それは非常に遅いためであり、これは多数の人によってパフォーマンスを改善することによってのみ修正することができます。 + 10%を指定しても、遅いソフトウェアが高速になることはありません。そして、通常、後の最適化で10%を超えることはありません。

したがって、ソフトウェアにとってパフォーマンスが重要である場合は、「ああ、遅いですが、後で改善することができます」と考えるのではなく、設計時に最初から考慮に入れる必要があります。できないから!

それはあなたの特定のケースに実際には適合しないことは知っていますが、それは「早期の最適化は本当にすべての悪の根源ですか?」という一般的な質問に答えます。 -明確なNO。

あらゆる機能などのすべての最適化は、慎重に設計および実装する必要があります。そしてそれは費用と利益の適切な評価を含みます。アルゴリズムを最適化して、測定可能なパフォーマンスの向上が得られない場合に、あちこちで数サイクルを節約しないでください。

例として:関数をインライン化することで関数のパフォーマンスを向上させ、数サイクルを節約できる可能性がありますが、同時に実行可能ファイルのサイズを大きくして、TLBとキャッシュミスの可能性を増やし、数千サイクルまたはさらにはページング操作。パフォーマンスが完全に失われます。これらのことを理解していないと、「最適化」がうまくいかない場合があります。

愚かな最適化は「時期尚早」の最適化よりも悪ですが、どちらも時期尚早の非最適化よりも優れています。

10
Timo

POには2つの問題があります。1つ目は、より多くの機能を記述したり、より多くのバグを修正したりするために使用できる非本質的な作業に使用される開発時間と、2つ目は、コードが効率的に実行されているという誤った安心感です。 POは、ボトルネックにならないコードを最適化することをしばしば含みますが、そうなるコードに気付くことはありません。 「時期尚早」ビットは、適切な測定を使用して問題が特定される前に最適化が行われることを意味します。

基本的には、はい、これは時期尚早の最適化のように聞こえますが、バグが発生しない限り、私は必ずしもそれを取り消すわけではありません-結局のところ、今は最適化されています(!)

6
harriyott

別の見方をすると、ほとんどのプログラマー/開発者は成功を計画しておらず、「プロトタイプ」はほとんど常にリリース1.0になるというのが私の経験です。上品でセクシー、そして高機能なフロントエンド(基本的にはUI)がユーザーの普及と熱意を広めた4つの個別のオリジナル製品を直接体験しました。これらの各製品では、特に大規模で要求の厳しい顧客が製品を採用し始めたので、パフォーマンスの問題が比較的短い時間(1〜2年)内に忍び込み始めました。すぐにパフォーマンスが問題リストを支配しましたが、新機能の開発が管理者の優先リストを支配しました。毎回のリリースで新しい機能が追加されたため、顧客はますます不満を募らせましたが、優れたサウンドでしたが、パフォーマンスの問題のためにほとんどアクセスできませんでした。

そのため、「プロトタイプ」ではほとんどまたはまったく懸念のない非常に基本的な設計および実装の欠陥が、製品(および企業)の長期的な成功の大きな障害になりました。

お客様のデモは、XML DOM、SQL Express、および多くのクライアント側のキャッシュデータを備えたラップトップで見栄えがよく、パフォーマンスが優れている場合があります。あなたが成功した場合、本番システムはおそらく書き込みをクラッシュさせます。

1976年、私たちはまだ平方根を計算したり、大きな配列をソートしたりする最適な方法について議論していました。ドンクヌースの格言は、問題の解決ではなく設計プロセスの早い段階でそのような低レベルルーチンの最適化に焦点を当てるという間違いに向けられていました。次に、コードのローカライズされた領域を最適化します。

効率的なコード(C++、VB、T-SQLなど)を記述しない、またはデータストアを適切に設計しない、またはネットワークアーキテクチャを考慮しないための言い訳として格言を繰り返す場合、IMOは単に私たちの仕事の本質の非常に浅い理解。レイ

3
Ray

コードの理解に問題はないため、このケースは例外と見なすことができます。

ただし、一般的に最適化はコードの可読性と理解性を低下させるため、必要な場合にのみ適用する必要があります。簡単な例-いくつかの要素のみをソートする必要があることがわかっている場合-BubbleSortを使用します。しかし、要素が増える可能性があり、その量がわからない場合は、QuickSort(たとえば)を使用した最適化は悪ではなく、必須です。また、これはプログラムの設計時に考慮する必要があります。

3
m_pGladiator

私はそれがマイクコーンがコードの「金メッキ」と呼んでいるものだと信じています。

彼はそれに反対した。

追伸「金メッキ」は、スペック的には機能の一種のベルアンドホイッスルである可能性があります。コードを見ると、それは不必要な最適化、「将来性のある」クラスなどの形をとっています。

3
Ilya Kochetov

既存のコードをより高速に書き直すと、時期尚早な最適化の問題が発生することがほとんどです。そもそも複雑な最適化を記述することが問題になる可能性があることはわかりますが、ほとんどの場合、壊れているもの(知られていないもの)を修正する際に醜い頭を育てる時期尚早の最適化が見られます。

そして、これの最悪の例は、誰かが標準ライブラリの機能を再実装しているのを見たときです。それは大きな赤旗です。同様に、組み込みコマンドが遅すぎるのではないかと心配したため、文字列操作用のカスタムルーチンを実装する人を見たことがあります。

その結果、コードの理解が困難になり(悪い)、作業に多くの時間を費やすことになり、おそらく役に立たない(悪い)ことになります。

3
jhocking

「PMO」(一部引用)に忠実な人のほとんどは、最適化は測定に基づく必要があり、測定は最後まで実行できないと述べています。

大規模なシステム開発での私の経験でもありますが、開発が完了に近づくにつれて、パフォーマンステストが最後に行われます。

これらの人々の「助言」に従うとすれば、すべてのシステムは耐え難いほど遅くなるでしょう。彼らのハードウェアのニーズは当初想定されていたよりもはるかに大きいため、それらも同様に高価になります。

開発プロセスでは定期的にパフォーマンステストを実施することを長い間提唱してきました。これは、新しいコードの存在(以前は存在しなかった)と既存のコードの状態の両方を示します。

  • 新しく実装されたコードのパフォーマンスは、既存の同様のコードのパフォーマンスと比較できます。新しいコードのパフォーマンスに対する「感触」は、今後確立されます。
  • 既存のコードが突然おかしくなった場合、何かが起こったことを理解しているので、システム全体に影響を与える(大部分の)後ではなく、すぐに調査できます。

もう1つのペットのアイデアは、ファンクションブロックレベルでソフトウェアを計測することです。システムが実行されると、機能ブロックの実行時間に関する情報が収集されます。システムのアップグレードを実行すると、以前のリリースと同じように機能ブロックがどのように機能するか、および機能ブロックが劣化したかどうかを判別できます。ソフトウェアの画面では、パフォーマンスデータはヘルプメニューからアクセスできます。

PMOが何を意味するのか、または意味がないのかについての this 優れた部分を確認してください。

1
Olof Forshell

「時期尚早」の定義次第ではないでしょうか。作成中に低レベルの機能をすばやく作成することは、本質的に悪ではありません。それは引用の誤解だと思います。時々私はその引用がいくつかのより多くの資格と関係があると思う。ただし、読みやすさに関するm_pGladiatorのコメントをエコーし​​ます。

1
Dominic Rodger

答えは次のとおりです。複雑なデータベースクエリなど、特定の種類の作業では効率が重要であることを主張します。他の多くの場合、コンピューターはほとんどの時間をユーザー入力の待機に費やしているため、ほとんどのコードを最適化することはせいぜい努力の無駄であり、最悪の場合は逆効果です。

場合によっては、効率またはパフォーマンス(知覚または実数)を考慮して設計できます。たとえば、適切なアルゴリズムを選択したり、ユーザーインターフェイスを設計したりして、特定の高価な操作がバックグラウンドで発生するようにします。多くの場合、ホットスポットを特定するためのプロファイリングまたはその他の操作により、10/90のメリットが得られます。

私が説明できるこの1つの例は、以前に約560のテーブルが含まれていた訴訟ケース管理システムに対して行ったデータモデルです。それは正規化された状態で始まり(ある大手5企業のコンサルタントがそれを入れたので「美しく正規化されました」)、それに4つの項目の非正規化データを入れるだけで済みました。

  • 検索画面をサポートする1​​つのマテリアライズドビュー

  • マテリアライズドビューでは実行できなかった別の検索画面をサポートするための1つのトリガー管理テーブル。

  • 1つの非正規化レポートテーブル(これは、データウェアハウスプロジェクトが缶詰になったときに、いくつかのスループットレポートを作成する必要があったためにのみ存在しました)

  • システム内の非常に多数の異種イベントのうち最新のものを検索する必要があったインターフェース用の1つのトリガー管理テーブル。

これは(当時)オーストラレーシアで最大のJ2EEプロジェクトであり、100年以上の開発者期間でした。データベーススキーマには4つの非正規化アイテムがあり、そのうちの1つは実際にはまったく属していませんでした。

時期尚早な最適化はすべての悪の根源ではありません、それは確かです。ただし、欠点があります。

  • 開発中にもっと時間を費やす
  • テストにもっと時間をかけます
  • 本来なら存在しないであろうバグを修正するためにより多くの時間を費やす

早期の最適化の代わりに、早期の可視性テストを実行して、より良い最適化が実際に必要かどうかを確認することができます。

1
Herr_Alien