コードを最適化して高速化するいくつかの手法を聞いたことがあります。
一方は明らかに関連性の高い最適化です。より優れたアルゴリズムの使用、ベンチマークなどです。
反対側には、関連性が疑わしい手法があります。それらが神話なのか現実なのか、単に廃止された使用法なのかわかりません。
私の質問は後者についてです。いくつかの例:
多くの場合、これらの手法は、読みやすく、理解しやすく、保守可能なコードベースに反しています。だから私は彼らが存在する根本的な理由があるかどうか知りたいのですが。
注意すべきいくつかの事柄:
私の質問:それらの実践は神話ですか、それとも現実ですか?パフォーマンス基準が厳しいアプリケーション(高頻度取引ソフトウェアなど)のコンテキストでは、それらのプラクティスは「優れた開発」プラクティスに取って代わることができますか?
パフォーマンスの最適化は、この種の一般化されたルールには向いておらず、提案したルールが最適化の優れた方法であったかどうかはわかりません。
これはより良い計画です:
特定のパフォーマンス要件を設定します。 (「十分に良い」と定義します)
コレクション実装の賢明な選択など、ルーチンのパフォーマンス最適化を利用する適切に記述されたコードから始めます。
プロファイラ を使用して、コードで ホットスポット を見つけます。これは、プロセッサが最も多くの時間を費やしている場所です。このコードは、コード全体の10%未満を表します。
パフォーマンスを向上させるために、ホットスポットのみを最適化します。
パフォーマンス要件が達成されるまで、手順3と4を繰り返します。
それでおしまい。
パフォーマンスが最も重要な場合は、「ベストプラクティス」に違反してパフォーマンスを達成できます。しかし、ほとんどの場合、そうする必要はありません。
「関数呼び出しのオーバーヘッドを回避するための大きな関数(数千行)」
私の経験では正反対です。コンパイラーは、巨大な関数に適したコードを作成する際に問題が発生する傾向があります。実行時間が本当に私にとって重要である例があり、小さなループを別々の関数に抽出することでかなり測定可能スピードアップ-同じタイムクリティカルループに対して生成されたコードそれが別の機能にあるときはより良かった。 (実際に起こったことは、単純なループがすべての変数をレジスターに持つ一方で、大きな関数内の同じループが同じ変数にスタックメモリを使用することでした)。
しかし、「高周波取引ソフトウェア」の鍵は他のデバイスとの高速通信であり、それは全く異なる技術を必要とします。
PS。コードを高速化する可能性が低いこれらのすべての提案を見るのは少し面倒ですが、コードの可読性と保守性が大幅に低下します。 C++開発者が知っておくべき簡単なトリックについてはどうでしょう。クラスTのインスタンスを含む標準ベクトルがあるとします。次に、これらの2つのループを比較します。
for (T t in vector) {}
for (T& t in vector) {}
最初のループでは、ベクター内のすべての単一アイテムのコピーが作成および破棄されます。 2つの潜在的に高価な操作。 2番目のループでは、そのようなことはまったく行われません。その単一文字「&」は、適切な状況下でループを100倍速くすることができます。
最適化に関する優れた実践を要約する3つの引用:
- 早期の最適化はすべての悪の根源です
ドナルドクヌース- コードを処理しないで、より良いアルゴリズムを見つける
Kernighan&Plauger- デバッグは、コードの作成よりも2倍困難です。したがって、コードをできる限り巧妙に記述した場合、当然のことながら、デバッグするほど賢くはありません。
B.W。Kernighan
残りは神話です。
あなたの例についてのそれほど重要ではないコメント:
register
を覚えている人はいますか?++i;
、i++;
およびi=i+1
を分離して生成 まったく同じコード を適切に最適化したコンパイラーで。そのため、++i
は最適化ではなくなりました。しかし、それは表現力の観点からはまだ有効です。しかし、これはスタイルの問題です。あなたはそれが好きかどうか。そうでない場合は、おそらくC++ はあなたにとって最も適した言語ではありません;-)C++に固有ではありませんが、最もよくある誤解の1つは、アルゴリズムの次のステップに進む前にすべてのデータをまとめると、処理が速くなるという考えです。これは、いくつかの方法で現れます。例えば:
これらのアプローチは、パフォーマンスの観点から見ると悲惨であり、一度設置すると取り除くのが難しい場合があります。これらの設計は、多くの場合、抽象化の境界を免れるため、より効率的なソリューションに移行するには、多くの場合、大幅な書き換えが必要です。たとえば、別のアプリケーションが使用する大きなドキュメントを生成している場合、ダウンストリームアプリケーションを変更せずに、それを小さな部分に分割することはできません。または、アプリケーションが既知のサイズの配列またはその他の型を想定して構築されている場合、それをイテレータで置き換えるのは必ずしも簡単ではありません。
このため、これは早い段階で検討する必要のあるものであり、パフォーマンスの問題が見つかるまで待つ一般的に良いアドバイスの例外と考えています。
良い答えはたくさんありますが、文字通りの質問に現在答えている人はいません
だから私は彼らが存在する根本的な理由があるかどうか知りたいのですが。
私が正しく覚えているように、これらの各テクニックには正当な理由がありました約20〜30年前遅いハードウェア、特にそれほど洗練されていないコンパイラまたはインタプリタ、マイクロ最適化でも必要なパフォーマンスの向上をもたらす可能性がある場合。
今日、C++、JavaまたはC#などの言語に最新のコンパイラーを使用している場合、質問にリストされている例が最適化されたコードで速度の利点をもたらすものは1つもないと思いますホットスポット:VBAのような一部の古いがまだ使用されている言語実装では、これらの手法の一部はまだ有効な場合があります-場合によっては。
今日でも、巨大なCPUパワーを持たず、通常はCでプログラムされている小さな組み込みデバイスがたくさんあります。これらのプラットフォーム用のCコンパイラーの最適化品質については、実際にはわかりませんが、あなたが言及したこれらの最適化手法のいくつかが依然として重要である場合があります。
しかし、他の人が指摘したように、「念のため」事前にマイクロ最適化を適用するno正当化があります。最も賢明なアプローチは
実際には、多くの場合、コードは十分に高速なので、何もしません。
それが十分に速くない場合は、多くの場合optimiseを行う必要はありません。完全に愚かでパフォーマンスが低下したことを理解し、それをやめなければなりません。それは、最近の私の経験です。何かが遅すぎるとき、それは誰かが愚かなことをしているためでした。 (「最適化」には何か賢いことを伴うと思うので、愚かなことをやめることは最適化として数えられません。)辞書を使用する代わりに逐次検索によって配列内のキーを検索するなど、間違ったデータ構造の選択はこのカテゴリに分類されます。
次のステップは、正当な理由なく繰り返し行う作業を探すことです。配列を繰り返しソートするようなものです。同じデータセットを繰り返し計算します。同じデータをデータベースから複数回読み取る、など。それを避けることはあなたに大きな節約を与えることができます。
そして、それが十分ではなく、本当の最適化が必要であると思われる場合:何をすべきかを知っている人を見つけます。
提案されているようなマイクロ最適化は、リストの最後にあります。
workを作成し、次にbeautifulを作成します。 90%の確率で、美しいものにすれば、すでに高速になります。だから本当に、ただ美しくしてください!
ジョー・アームストロング