web-dev-qa-db-ja.com

最適化手法の関連性

コードを最適化して高速化するいくつかの手法を聞いたことがあります。

  • 一方は明らかに関連性の高い最適化です。より優れたアルゴリズムの使用、ベンチマークなどです。

  • 反対側には、関連性が疑わしい手法があります。それらが神話なのか現実なのか、単に廃止された使用法なのかわかりません。

私の質問は後者についてです。いくつかの例:

  • 関数呼び出しのオーバーヘッドを回避するための大きな関数(数千行)
  • オブジェクトのオーバーヘッドを回避するSRPなし
  • スコープ付き変数を使用する代わりに、変数をできるだけ再利用します
  • ++ i/i ++
  • そして、他のいくつかの実践。

多くの場合、これらの手法は、読みやすく、理解しやすく、保守可能なコードベースに反しています。だから私は彼らが存在する根本的な理由があるかどうか知りたいのですが。

注意すべきいくつかの事柄:

  • 私はルールが存在する可能性があることを想定しているだけであり、それらはコードや開発者の心に根付いた悪い習慣にすぎないというわけではありません。
  • 私は現在C++ 98で作業しており、それに応じて古いコンパイラーを使用しているので、c ++ 14で次の将来にはさらに最新のコンパイラーを使用できると期待しています。答えが環境に依存するかどうか知りたい

私の質問:それらの実践は神話ですか、それとも現実ですか?パフォーマンス基準が厳しいアプリケーション(高頻度取引ソフトウェアなど)のコンテキストでは、それらのプラクティスは「優れた開発」プラクティスに取って代わることができますか?

7
JayZ

パフォーマンスの最適化は、この種の一般化されたルールには向いておらず、提案したルールが最適化の優れた方法であったかどうかはわかりません。

これはより良い計画です:

  1. 特定のパフォーマンス要件を設定します。 (「十分に良い」と定義します)

  2. コレクション実装の賢明な選択など、ルーチンのパフォーマンス最適化を利用する適切に記述されたコードから始めます。

  3. プロファイラ を使用して、コードで ホットスポット を見つけます。これは、プロセッサが最も多くの時間を費やしている場所です。このコードは、コード全体の10%未満を表します。

  4. パフォーマンスを向上させるために、ホットスポットのみを最適化します。

  5. パフォーマンス要件が達成されるまで、手順3と4を繰り返します。

それでおしまい。

パフォーマンスが最も重要な場合は、「ベストプラクティス」に違反してパフォーマンスを達成できます。しかし、ほとんどの場合、そうする必要はありません。

20
Robert Harvey

「関数呼び出しのオーバーヘッドを回避するための大きな関数(数千行)」

私の経験では正反対です。コンパイラーは、巨大な関数に適したコードを作成する際に問題が発生する傾向があります。実行時間が本当に私にとって重要である例があり、小さなループを別々の関数に抽出することでかなり測定可能スピードアップ-同じタイムクリティカルループに対して生成されたコードそれが別の機能にあるときはより良かった。 (実際に起こったことは、単純なループがすべての変数をレジスターに持つ一方で、大きな関数内の同じループが同じ変数にスタックメモリを使用することでした)。

しかし、「高周波取引ソフトウェア」の鍵は他のデバイスとの高速通信であり、それは全く異なる技術を必要とします。

PS。コードを高速化する可能性が低いこれらのすべての提案を見るのは少し面倒ですが、コードの可読性と保守性が大幅に低下します。 C++開発者が知っておくべき簡単なトリックについてはどうでしょう。クラスTのインスタンスを含む標準ベクトルがあるとします。次に、これらの2つのループを比較します。

for (T t in vector) {}
for (T& t in vector) {}

最初のループでは、ベクター内のすべての単一アイテムのコピーが作成および破棄されます。 2つの潜在的に高価な操作。 2番目のループでは、そのようなことはまったく行われません。その単一文字「&」は、適切な状況下でループを100倍速くすることができます。

5
gnasher729

最適化に関する優れた実践を要約する3つの引用:

  • 早期の最適化はすべての悪の根源です
    ドナルドクヌース
  • コードを処理しないで、より良いアルゴリズムを見つける
    Kernighan&Plauger
  • デバッグは、コードの作成よりも2倍困難です。したがって、コードをできる限り巧妙に記述した場合、当然のことながら、デバッグするほど賢くはありません。
    B.W。Kernighan

残りは神話です。

あなたの例についてのそれほど重要ではないコメント:

  • 数千行の関数を分解して2、3の関数呼び出しを回避しないと、パフォーマンスの焦点は命令の0.1%になります。つまり、パフォーマンスの問題が他の場所にある確率は99.1%です。
  • とにかく、グローバルオプティマイザやインライン展開の時代には、もう少し時代遅れではないでしょうか。 30年前の手作りの最適化の要約であったキーワードregisterを覚えている人はいますか?
  • オブジェクトのオーバーヘッドを回避するSRPは有効ではありませんが、SRPの誤解が生じた場合のみです。 SRPはボブおじさんによるものです-コンセプトの発明者-変更する(人間の)理由について 、関数の数を減らすためのクラスの乗算についてではありません(極端にするとOOP手続き型プログラミングの一種)。実際のSRPの利点は、IMHOの欠点をはるかに上回ります。
  • ++i;i++;およびi=i+1を分離して生成 まったく同じコード を適切に最適化したコンパイラーで。そのため、++iは最適化ではなくなりました。しかし、それは表現力の観点からはまだ有効です。しかし、これはスタイルの問題です。あなたはそれが好きかどうか。そうでない場合は、おそらくC++ はあなたにとって最も適した言語ではありません;-)
  • 他の多くの人間のマイクロ最適化は、現在、オプティマイザよりも優れています。オプティマイザは、ターゲットCPUの深い知識を活用して、いくつかの代替案を詳細に分析できます。しかし、より良いアルゴリズムを見つけることはまだ私たちのためです。私はこの分野で長い間AIよりも優れていることを願っています(いつの日か気になりますが)。
5
Christophe

C++に固有ではありませんが、最もよくある誤解の1つは、アルゴリズムの次のステップに進む前にすべてのデータをまとめると、処理が速くなるという考えです。これは、いくつかの方法で現れます。例えば:

  • 巨大なデータメッセージの作成
  • 大きなデータ構造をメモリにロードする
  • セット内のすべてのアイテムに対してプロセスAを実行し、次にすべてのアイテムに対してプロセスBを実行する、など。

これらのアプローチは、パフォーマンスの観点から見ると悲惨であり、一度設置すると取り除くのが難しい場合があります。これらの設計は、多くの場合、抽象化の境界を免れるため、より効率的なソリューションに移行するには、多くの場合、大幅な書き換えが必要です。たとえば、別のアプリケーションが使用する大きなドキュメントを生成している場合、ダウンストリームアプリケーションを変更せずに、それを小さな部分に分割することはできません。または、アプリケーションが既知のサイズの配列またはその他の型を想定して構築されている場合、それをイテレータで置き換えるのは必ずしも簡単ではありません。

このため、これは早い段階で検討する必要のあるものであり、パフォーマンスの問題が見つかるまで待つ一般的に良いアドバイスの例外と考えています。

2
JimmyJames

良い答えはたくさんありますが、文字通りの質問に現在答えている人はいません

だから私は彼らが存在する根本的な理由があるかどうか知りたいのですが。

私が正しく覚えているように、これらの各テクニックには正当な理由がありました約20〜30年前遅いハードウェア、特にそれほど洗練されていないコンパイラまたはインタプリタ、マイクロ最適化でも必要なパフォーマンスの向上をもたらす可能性がある場合。

今日、C++、JavaまたはC#などの言語に最新のコンパイラーを使用している場合、質問にリストされている例が最適化されたコードで速度の利点をもたらすものは1つもないと思いますホットスポット:VBAのような一部の古いがまだ使用されている言語実装では、これらの手法の一部はまだ有効な場合があります-場合によっては。

今日でも、巨大なCPUパワーを持たず、通常はCでプログラムされている小さな組み込みデバイスがたくさんあります。これらのプラットフォーム用のCコンパイラーの最適化品質については、実際にはわかりませんが、あなたが言及したこれらの最適化手法のいくつかが依然として重要である場合があります。

しかし、他の人が指摘したように、「念のため」事前にマイクロ最適化を適用するno正当化があります。最も賢明なアプローチは

  • コードをできるだけきれいに実装する
  • その後最適化し、関連する部分のみ
  • 証明された(=測定された)メリットを実際にもたらした最適化のみを保持する
2
Doc Brown

実際には、多くの場合、コードは十分に高速なので、何もしません。

それが十分に速くない場合は、多くの場合optimiseを行う必要はありません。完全に愚かでパフォーマンスが低下したことを理解し、それをやめなければなりません。それは、最近の私の経験です。何かが遅すぎるとき、それは誰かが愚かなことをしているためでした。 (「最適化」には何か賢いことを伴うと思うので、愚かなことをやめることは最適化として数えられません。)辞書を使用する代わりに逐次検索によって配列内のキーを検索するなど、間違ったデータ構造の選択はこのカテゴリに分類されます。

次のステップは、正当な理由なく繰り返し行う作業を探すことです。配列を繰り返しソートするようなものです。同じデータセットを繰り返し計算します。同じデータをデータベースから複数回読み取る、など。それを避けることはあなたに大きな節約を与えることができます。

そして、それが十分ではなく、本当の最適化が必要であると思われる場合:何をすべきかを知っている人を見つけます。

提案されているようなマイクロ最適化は、リストの最後にあります。

1
gnasher729

workを作成し、次にbeautifulを作成します。 90%の確率で、美しいものにすれば、すでに高速になります。だから本当に、ただ美しくしてください!

ジョー・アームストロング

1
laike9m