web-dev-qa-db-ja.com

最適化は時期尚早ですか?

クヌースが言ったように、

小さな効率については忘れてください。たとえば、97%の時間です。時期尚早な最適化は、すべての悪の根源です。

これは、Stack Overflowでよく出てくるもので、「最も効率的なループメカニズム」、「SQL最適化手法」などの質問に対する答えです。 ( など )。これらの最適化のヒントの質問に対する標準的な答えは、コードをプロファイリングし、それが最初に問題であるかどうかを確認することです。問題がない場合は、新しいテクニックは不要です。

私の質問は、特定の手法が異なるが、特に不明瞭または難読化されていない場合、それは本当に時期尚早の最適化と見なすことができるかということです。

これは、Randall HydeによるThe Fallacy of Premature Optimizationと呼ばれる関連記事です。

78
nickf

私見では、最適化の90%は、想定される現在、さらに重要なことには将来の要件に基づいて、設計段階で行う必要があります。アプリケーションが必要な負荷に対応していないためにプロファイラーを取り出さなければならない場合、それを遅らせすぎると、IMOは問題の修正に失敗する一方で多くの時間と労力を浪費します。

通常、価値のある唯一の最適化は、速度の点でパフォーマンスを大幅に改善するもの、またはストレージや帯域幅の点で乗数を得るものです。これらのタイプの最適化は通常、アルゴリズムの選択とストレージ戦略に関連しており、既存のコードに戻すことは非常に困難です。これらは、システムを実装する言語の決定に影響を与えるほど深くなる場合があります。

私のアドバイスは、コードではなく要件に基づいて早期に最適化し、アプリの可能な寿命を延ばすことです。

40
SmacL

あなたがプロファイリングしていない場合、それは時期尚早です。

30
JaredPar

私の質問は、特定の手法が異なるが、特に不明瞭または難読化されていない場合、それは本当に時期尚早の最適化と見なすことができるかということです。

ええと... 2つの手法が用意されており、コストは同じ(使用、読み取り、変更に同じ労力)で、1つはより効率的です。いいえ、その場合、より効率的なものを使用しても、時期尚早ではありません。

コードの書き込みを中断して、一般的なプログラミングコンストラクト/ライブラリルーチンに代わる手段を探す機会をオフにして、どこにでも効率的なバージョンがぶら下がっている場合でも、書いているものの相対的な速度は実際には重要ではないことを知っています。 .. それ時期尚早。

27
Shog9

これは、時期尚早な最適化を回避するという全体的な概念で私が目にする問題です。

それを言うこととそれを行うことの間には断絶があります。

私は多くのパフォーマンスチューニングを行っており、そうでなければうまく設計されたコードから大きな要因を絞り出しました。 ここに例があります。

ほとんどすべての場合、パフォーマンスが最適とは言えない理由は、私がギャロッピングの一般性と呼んでいるものです。これは、抽象マルチレイヤークラスと完全なオブジェクトの使用です。単純な概念はエレガントですが完全に十分である指向のデザイン。

また、通知駆動型アーキテクチャや、オブジェクトのブールプロパティを設定するだけでアクティビティに無限の波及効果をもたらすことができる情報非表示など、これらの抽象的な設計概念が教えられている教材では、理由は何ですか? 効率

それで、それは時期尚早の最適化でしたか?

10
Mike Dunlavey

まず、コードを動作させます。次に、コードが正しいことを確認します。第三に、それを速くします。

ステージ3の前に行われるコードchangeは、明らかに時期尚早です。その前に行われた設計の選択をどのように分類するか(完全に適切なデータ構造を使用するなど)は完全にはわかりませんが、パフォーマンスが優れている人よりも、プログラミングが簡単な抽象化を使用する方が好きです。プロファイリングの使用を開始し、結果を比較するための正しい(しばしば低速ですが)リファレンス実装を用意できる段階。

8
Vatine

あなたが話しているように見えるのは、ハッシュベースのルックアップコンテナーを使用するような最適化です。多くのキールックアップが行われる場合は、配列のようなインデックス付きコンテナーを使用します。これはnot時期尚早の最適化ですが、設計段階で決定する必要があります。

Knuthルールに関する最適化の種類は、最も一般的なコードパスの長さを最小化することです。たとえば、アセンブリでの書き換えやコードの簡略化などにより、最も一般的に実行されるコードを最適化し、一般性を低くします。しかし、コードのどの部分にこの種の最適化が必要であるかが判明するまでこれを実行しても意味がありません。最適化によってコードの理解や維持が困難になる場合があります。

Knuthはまた、プログラムが使用するアルゴリズム(問題へのアプローチ)を最適化するのではなく、変更する方が常に良いと述べています。たとえば、少し調整すると最適化の速度が10%向上する可能性がありますが、プログラムの動作方法を根本的に変更すると10倍速くなる可能性があります。

この質問に投稿された他の多くのコメントへの反応:アルゴリズムの選択!=最適化

7
FredV

データベースの観点からは、設計段階で最適な設計を考慮しないことは、せいぜい無茶苦茶です。データベースは簡単にリファクタリングされません。それらが十分に設計されていない場合(これは、最適化を考慮しない設計は、時期尚早な最適化のナンセンスの背後にどのように隠そうとするかもしれないものです)、データベースがあまりにも基本的であるため、ほとんど回復できませんシステム全体の操作。ユーザーが100万人になり、アプリケーション全体でカーソルを使用したために人々が叫ぶまで待つよりも、予想される状況に最適なコードを適切に検討して設計する方がはるかにコストがかかりません。 sargeableコードの使用、可能な限り最適なインデックスとなるものの選択など、その他の最適化は、設計時にのみ意味があります。早くて汚いというのがその理由です。これはうまく機能しないため、優れたコードの代わりにクイックネスを使用しないでください。また、率直に言って、データベースでのパフォーマンスチューニングを理解すると、同じ時間内に、またはパフォーマンスの悪いコードよりもパフォーマンスが高い可能性が高いコードを作成できます。時間をかけてパフォーマンスの良いデータベース設計を学ぶのは、開発者の怠惰であり、ベストプラクティスではありません。

6
HLGEM

格言の要点は通常であり、最適化は複雑で複雑です。そして通常、あなたはアーキテクト/デザイナー/プログラマー/メンテナーが何が起こっているのかを理解するために明確で簡潔なコードを必要とします。

特定の最適化が明確で簡潔な場合は、自由に試してみてください(ただし、戻ってその最適化が有効かどうかを確認してください)。重要なのは、パフォーマンスのメリットが、最適化の記述と維持に伴うコストを上回るまで、開発プロセス全体を通じてコードを明確かつ簡潔にすることです。

5
yfeldblum

最適化は、非常に高いレベルから非常に低いレベルまで、さまざまなレベルの粒度で発生する可能性があります。

  1. 優れたアーキテクチャ、疎結合、モジュール性などから始めます。

  2. 問題に適したデータ構造とアルゴリズムを選択します。

  3. より多くのコード/データをキャッシュに収めようとして、メモリを最適化します。メモリサブシステムは、CPUの10〜100倍の速度が遅く、データがディスクにページングされると、1000〜10,000倍遅くなります。メモリの消費に注意することは、個々の命令を最適化するよりも大きな利益をもたらす可能性が高くなります。

  4. 各関数内で、フロー制御ステートメントを適切に使用します。 (不変式をループ本体の外に移動します。最も一般的な値を最初にスイッチ/ケースなどに配置します。)

  5. 各ステートメント内で、正しい結果が得られる最も効率的な式を使用してください。 (乗算とシフトなど)

除算式とシフト式のどちらを使用するかについての注意を引くことは、必ずしも時期尚早の最適化ではありません。最初にアーキテクチャ、データ構造、アルゴリズム、メモリフットプリント、およびフロー制御を最適化せずに行う場合、これは時期尚早です。

そしてもちろん、any最適化は、目標パフォーマンスのしきい値を定義しないと時期尚早です。

ほとんどの場合、次のいずれかです。

A)高レベルの最適化を実行することで目標パフォーマンスのしきい値に到達できるため、式をいじる必要はありません。

または

B)可能なすべての最適化を実行した後でも、目標パフォーマンスのしきい値を満たせず、低レベルの最適化では、読みやすさの損失を正当化するのに十分なパフォーマンスの違いがありません。

私の経験では、ほとんどの最適化問題は、アーキテクチャ/設計またはデータ構造/アルゴリズムレベルで解決できます。メモリフットプリントの最適化は、(常にではありませんが)しばしば要求されます。ただし、フロー制御と式のロジックを最適化する必要はめったにありません。そして、それが実際に必要な場合は、それで十分であることはめったにありません。

4
benjismith

プログラミングを行う場合、いくつかのパラメータが重要です。これらの中で:

  • 読みやすさ
  • 保守性
  • 複雑
  • 堅牢性
  • 正しさ
  • パフォーマンス
  • 開発時間

最適化(パフォーマンスの向上)は他のパラメーターを犠牲にして行われることが多く、これらの領域の「損失」とのバランスを取る必要があります。

よく機能する既知のアルゴリズムを選択するオプションがある場合、事前に「最適化」するコストは許容範囲であることがよくあります。

4
Ola Eldøy

パフォーマンスの問題が確認されたときにのみ最適化を試みます。

私の時期尚早な最適化の定義は、「パフォーマンスの問題であることがわかっていないコードに費やされた労力」です。最適化の時間と場所は間違いなくあります。ただし、コツは、アプリケーションのパフォーマンスに影響する場合と、追加のコストがパフォーマンスへの影響を上回る場合にのみ、追加のコストを費やすことです。

コード(またはDBクエリ)を作成するときは、「効率的な」コード(つまり、目的の機能を実行し、最も単純なロジックで合理的で迅速かつ完全に実行するコード)を作成するように努めます。コード。多くの場合、最適化によってコードがさらに複雑になり、そのコードの開発コストと保守コストの両方が増加します。

私のアドバイス:メリットを数値化できる場合にのみ、最適化のコストを支払うようにしてください。

4
Chris Nava

プロファイラーを使用する必要性は、極端な場合のために残されるべきです。プロジェクトのエンジニアは、パフォーマンスのボトルネックがどこにあるかを認識する必要があります。

「時期尚早の最適化」は信じられないほど主観的だと思います。

コードを書いていて、ハッシュテーブルを使用する必要があるとknowしている場合は、それを行います。私はそれを欠陥のある方法で実装せず、誰かがそれに問題を抱えているときに、バグレポートが1か月または1年後に届くのを待ちます。

再設計は、最初から明白な方法で設計を最適化するよりもコストがかかります。

明らかに、最初にいくつかの小さなことを見落とすでしょうが、これらが設計上の重要な決定になることはめったにありません。

したがって、デザインを最適化しないことは、コード自体にIMOコードの匂いがすることです。

2
nbevans

ノーマンの答えは素晴らしい。どういうわけか、あなたは日常的にいくつかの「時期尚早の最適化」を行いますが、そうしないと完全に非効率的であることがわかっているためです。

たとえば、ノーマンのリストに追加するには:

  • Java(またはC#など)で、String + String(ループ内)の代わりにStringBuilder連結を使用します。
  • 次のようにCでループするのを避けます:for (i = 0; i < strlen(str); i++)(ここでstrlenは、各ループで呼び出される、文字列を毎回歩く関数呼び出しであるため);
  • ほとんどのJavaScript実装では、for (i = 0 l = str.length; i < l; i++)を実行する方が高速で、読みやすいため、問題ありません。

等々。しかし、そのようなマイクロ最適化は、コードの読みやすさを犠牲にしてはなりません。

2
PhiLho

クヌースの最初の引用は、ホットスポットを排除する方法として慎重に選択および測定された領域でのgotoの使用を促進することを彼が書いた論文から来たことは注目に値します。彼の引用は、これらの重要なループをスピードアップするためにgotoを使用する理由を正当化するために追加した警告でした。

[...]繰り返しますが、これは、たとえばnの平均値が約20であり、プログラムで検索ルーチンが約100万回実行される場合、全体の実行速度の顕著な節約になります。このようなループの最適化は、[gotos]を使用して学習することは難しくありません。前述したように、これらはプログラムのごく一部に適していますが、かなりの節約になります。 [...]

そして続けます:

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

彼がどのように「最適化」を引用符で使用したかを覚えておいてください(ソフトウェアはおそらく実際には効率的ではありません)。また、彼がこれらの「ペニーワイズアンドポンド愚かな」プログラマーを批判しているだけでなく、小さな非効率を常に無視すべきだと提案して反応する人々にも注意してください。最後に、頻繁に引用される部分に:

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

...そして、プロファイリングツールの重要性についてもう少し:

多くの場合、測定ツールを使用してきたプログラマーの普遍的な経験は、直感的な推測が失敗するため、プログラムのどの部分が本当に重要であるかについて事前に判断することは誤りです。このようなツールを7年間使用した後、これから作成するすべてのコンパイラは、プログラムのどの部分が最もコストが高いかを示すフィードバックをすべてのプログラマに提供するように設計する必要があると確信しました。実際、このフィードバックは、特にオフにしない限り、自動的に提供されます。

人々は彼の引用をあちこちで誤用し、彼の論文全体がマイクロ最適化を提唱しているとき、マイクロ最適化は時期尚早であることをしばしば示唆しています!彼が批判している人々のグループの1つは、この「従来の知恵」を繰り返し、小企業では常に効率を無視しているため、もともとは一部のマイクロオプティマイゼーションのあらゆる形態を阻止するようなタイプに向けられていた彼の引用を誤用している。

それでも、プロファイラーを持っている経験豊富な手が使用するときに適切に適用されたマイクロ最適化を支持することを支持する引用でした。今日の類推は、「ソフトウェアの最適化に盲目的に取り組むべきではありませんが、参照の局所性を改善するために主要な領域にカスタムメモリアロケータを適用すると、大きな違いが生じる可能性があります。」または、「SoA担当者を使用した手書きのSIMDコードは維持するのが本当に難しく、どこでも使用するべきではありませんが、適切に適用すると、メモリをより速く消費する可能性があります。経験豊富でガイド付きの手。 "

Knuthが上記で宣伝したように、慎重に適用されたマイクロ最適化を宣伝しようとするときはいつでも、免責事項を入れて、初心者が興奮して過度に興奮して、_goto。それは彼がやっていたことの一部です。彼の引用は事実上、大きな免責事項の一部でした。まるで、バイクが燃える火の穴を飛び越えている誰かが、自宅でこれを試してはいけないという免責事項を追加すると同時に、適切な知識と機器なしで試して怪我をした人を批判するのと同じです。 。

彼が「時期尚早の最適化」と見なしたのは、彼らが何をしているかを効果的に知らない人々によって適用された最適化でした:最適化が本当に必要かどうか、適切なツールで測定しなかった、おそらくの性質を理解していなかった彼らのコンパイラまたはコンピュータアーキテクチャ、そして何よりも、「ペニーワイズアンドポンド愚か」でした。つまり、彼らはペニーをピンチしようとすることで最適化(数百万ドルを節約)する大きな機会を見逃しており、すべてがコードの作成中により効果的にデバッグおよび保守します。

「ペニーワイズアンドポンド愚かな」カテゴリに当てはまらない場合、クリティカルループを高速化するためにgotoを使用している場合でも、Knuthの標準では時期尚早に最適化されていません。 (今日のオプティマイザに対して大いに役立つ可能性は低いものですが、もしそれが本当に重要な領域であったとしても、時期尚早に最適化することはできません)。本当に必要な領域で実際に行っていることをすべて適用していて、それらが本当にそれから利益を得るなら、あなたはKnuthの目でちょうど素晴らしいことをしています。

2
Dragon Energy

認識されているベストプラクティスが時期尚早な最適化であるとは思わない。それは、使用シナリオに応じて潜在的なパフォーマンスの問題であるwhat ifに時間を費やすことに関するものです。良い例:ボトルネックであることを証明する前に、オブジェクトの反射を最適化しようとして1週間を費やした場合、時期尚早に最適化していることになります。

1
Daniel Auger

ユーザーまたはビジネスのニーズにより、アプリケーションからより多くのパフォーマンスが必要であることが判明しない限り、最適化について心配する理由はほとんどありません。それでも、コードをプロファイルするまで何もしないでください。次に、最も時間がかかる部分を攻撃します。

1
Kamil Kisiel

私にとって時期尚早な最適化とは、システムが動作する前に、実際にプロファイルを作成してボトルネックがどこにあるかを知る前に、コードの効率を改善しようとすることです。それでも、多くの場合、可読性と保守性は最適化の前に来る必要があります。

1
PolyThinker

同様の質問に投稿したように、最適化のルールは次のとおりです。

1)最適化しないでください

2)(エキスパートのみ)後で最適化

最適化は時期尚早ですか?通常。

例外は、おそらく設計にあるか、頻繁に使用されるよくカプセル化されたコードにあります。過去に私はいくつかのタイムクリティカルなコード(RSA実装)に取り組みました。コンパイラーが生成したアセンブラーを調べ、内部ループ内の単一の不要な命令を削除すると、30%高速化しました。しかし、より洗練されたアルゴリズムを使用することによるスピードアップは、それよりも桁違いに大きかった。

「ここで300ボーモデムを最適化するのと同等のことをしていますか?」です。言い換えれば、ムーアの法則により、やがて最適化が無意味になります。スケーリングに関する多くの問題は、問題にハードウェアを追加するだけで解決できます。

最後に、プログラムが遅くなりすぎる前に最適化するのは時期尚早です。話しているWebアプリケーションの場合は、負荷のかかった状態で実行してボトルネックがどこにあるかを確認できます。ただし、他のほとんどのサイトと同じスケーリングの問題が発生し、同じソリューションが適用される可能性があります。

編集:ちなみに、リンクされた記事に関しては、私が行った仮定の多くに疑問を投げかけます。第一に、ムーアの法則が90年代に機能しなくなったことは真実ではありません。第二に、ユーザーの時間がプログラマーの時間よりも価値があることは明らかではありません。ほとんどのユーザーは(控えめに言っても)使用可能なすべてのCPUサイクルを必死に使用しているわけではなく、おそらくネットワークが何かを行うのを待っています。さらに、プログラマーの時間が他の何かを実装することから、ユーザーが電話をかけている間にプログラムが行う何かを数ミリ秒削ることに転用する場合には、機会費用があります。それより長いものは通常最適化されません、それはバグ修正です

0
frankodwyer

私の考えでは、さまざまなシナリオでどれだけのパフォーマンスを得ることができるかを知らずに何かを最適化すると、IS時期尚早の最適化になります。コードの目標は、人間が実際に最も読みやすくすることです。

0
Hao Wooi Lim