私の人生では、先生がその日言ったことを正確に思い出せません。おそらくあなたが知っていることを望んでいます。
モジュールは「データ構造とアルゴリズム」であり、彼は次の行に沿って何かを教えてくれました。
if
ステートメントは、最も高価な[何か]です。 [何か]は[何か]を登録します。
はい、恐ろしい思い出があり、本当にすみませんが、何時間もグーグルで遊んでいて、何も起こりません。何か案は?
「高価」は非常に相対的な用語です。特に「if
」ステートメントとの関係では、条件のコストも考慮する必要があるためです。それは、いくつかの短いcpu命令から、リモートデータベースを呼び出す関数の結果のテストまで、さまざまです。
私はそれを心配しません。組み込みプログラミングを行っているのでない限り、おそらく「if
」のコストを心配する必要はありません。ほとんどのプログラマーにとって、everがアプリのパフォーマンスを左右する要因ではありません。
特にRISCアーキテクチャのマイクロプロセッサのブランチは、最も高価な命令の一部です。これは、多くのアーキテクチャでコンパイラが実行パスが最も可能性が高いと予測し、それらの命令を実行可能ファイルに次に配置するため、分岐が発生したときにすでにCPUキャッシュにあるためです。ブランチが逆の場合、メインメモリに戻って新しい命令をフェッチする必要があります。これはかなり高価です。多くのRISCアーキテクチャでは、分岐を除くすべての命令が1サイクルです(多くの場合2サイクルです)。ここでは大きなコストについて話していないので、心配する必要はありません。また、コンパイラは99%の時間よりも最適化を最適化します:) EPICアーキテクチャ(Itaniumの例)について本当に素晴らしいことの1つは、ブランチの両側からの命令をキャッシュ(および処理開始)することです。その後、ブランチの結果が判明したら、不要なセットを破棄します。これにより、予期しないパスに沿って分岐する場合に、典型的なアーキテクチャの余分なメモリアクセスが節約されます。
セルパフォーマンスに関する記事 ブランチの削除によるパフォーマンスの向上 をご覧ください。別の楽しいものは ブランチレス選択に関するこの投稿 リアルタイム衝突検出ブログです。
この質問への回答として既に投稿された優れた回答に加えて、「if」ステートメントは高価な低レベル操作と見なされますが、より高いレベルの環境でブランチフリープログラミング技術を利用しようとすることを思い出してください。 (言語に関係なく)スクリプト言語やビジネスロジックレイヤーなどは、とんでもなく不適切な場合があります。
ほとんどの場合、プログラムは最初に明確にするために作成し、次にパフォーマンスのために最適化する必要があります。パフォーマンスが最重要である多くの問題領域がありますが、単純な事実は、ほとんどの開発者がレンダリングエンジンのコアの奥深くで使用するモジュールや、何週間も実行する高性能の流体力学シミュレーションを書いていないことです。ソリューションが「正しく機能する」ことを最優先事項とする場合、最後に考えるのは、コード内の条件ステートメントのオーバーヘッドを節約できるかどうかです。
可能な最低レベルでif
は(特定のif
のすべてのアプリ固有の前提条件を計算した後)で構成されます。
それに関連する費用:
ジャンプが高価な理由:
まとめると:
if
自体はnot遅いです。スローネスは常に相対的なものです。私はあなたがif文の「オーバーヘッド」を感じたことがないという私の人生に賭けています。高性能なコードを作成する場合は、とにかく分岐を避けたいかもしれません。 if
を遅くするのは、プロセッサが何らかのヒューリスティックな方法などに基づいてif
の後ろからコードをプリロードしていることです。また、プロセッサはどのパスがとられるかをまだ知らないため、パイプラインがマシンコードのif
分岐命令の直後にコードを実行するのを停止します(パイプラインプロセッサでは、複数の命令がインターリーブされて実行されます) 。実行されたコードは逆に実行する必要があります(他のブランチが取得された場合。branch misprediction
)、またはこれらの場所でnoop
を埋めて、これが起こらないようにします。
if
が悪の場合、switch
も悪であり、&&
、||
も。心配しないでください。
分岐によってCPU命令のプリフェッチが強制終了される可能性がありますか?
最近のプロセッサには長い実行パイプラインがあります。つまり、複数の命令がさまざまな段階で同時に実行されます。次の命令が実行を開始したときに、ある命令の結果を常に把握しているとは限りません。条件付きジャンプ(if)に遭遇すると、パイプラインが空になるまで待機してから、命令ポインタがどの方向に進むべきかを知る必要があります。
長い貨物列車だと思います。大量の貨物を直線で高速に運ぶことができますが、コーナーはひどく曲がります。
Pentium 4(プレスコット)には、31ステージの有名な長いパイプラインがありました。
Wikipedia の詳細
多くの人が指摘したように、現代のコンピューターでは条件分岐は非常に遅くなる可能性があります。
そうは言っても、ifステートメントには存在しない条件付きブランチは非常に多く、コンパイラーが何を思い付くのかが常にわかるわけではありません。する。 (コンパイラが何を確実に生成するかを判断できる場合、最適な最適化コンパイラがない可能性があります。)
私がこれが参照していると想像できる唯一のことは、if
ステートメントが一般に分岐をもたらす可能性があるという事実です。プロセッサアーキテクチャの仕様に応じて、分岐はパイプラインのストールやその他の最適な状況を引き起こします。
ただし、これは非常に状況に固有です。最新のプロセッサのほとんどは、分岐の悪影響を最小限に抑えるための分岐予測機能を備えています。別の例は、ARMアーキテクチャ(およびおそらく他の)が条件付きロジックを処理する方法です。ARMには命令レベルの条件付き実行があるため、単純な条件付きロジックは分岐しません。条件が満たされていません。
とにかく、このことを心配する前にロジックを修正してください。不正なコードは、可能な限り最適化されていません。
また、ループ内ではnotが非常に高価であることに注意してください。
現代のCPUは、if文を初めて訪れたときに、「if-body」を取得することを前提としています(または、別の言い方をすると、ループ本体を複数回取得することも想定しています)(*)。 2回目以降のアクセスでは、(CPU)がブランチ履歴テーブルを調べて、最後に条件がどのようになっていたかを確認できます(trueでしたか?falseでしたか?)。最後にfalseだった場合、投機的実行はifの「else」に進むか、ループを超えます。
(*)ルールは実際には「前方分岐はとられず、後方分岐はとられる」です。 if-statementには、条件がfalseと評価された場合に、only [前方]ジャンプ(if-bodyの後のポイントへ)があります(CPUはとにかく分岐/ジャンプを取得します)が、ループ内では、ループの後の位置への前方分岐(実行されない)と、反復時の後方分岐(実行される)があります。
これは、仮想関数またはfunction-pointer-callの呼び出しが多くの人が想定しているほど悪くない理由の1つでもあります( http://phresnel.org/blog/ )
CPUは深くパイプライン化されています。分岐命令(if/for/while/switch/etc)は、CPUが次にロードして実行する命令を実際に知らないことを意味します。
CPUは、何をすべきかを待機している間にストールするか、CPUが推測を行います。古いCPUの場合、または推測が間違っている場合、正しい命令をロードしてロードする間にパイプラインが停止する必要があります。 CPUによっては、10〜20命令のストールに相当する場合があります。
現代のCPUは、適切な分岐予測を行い、複数のパスを同時に実行し、実際のパスのみを保持することで、これを回避しようとします。これは大いに役立ちますが、ここまでしかできません。
クラスで頑張ってください。
また、実生活でこれを心配する必要がある場合は、おそらくOSデザイン、リアルタイムグラフィックス、科学計算、または同様のCPUにバインドされた何かをしているでしょう。心配する前にプロファイルします。
明らかに非効率的ではない、最も明確でシンプルでクリーンな方法でプログラムを作成してください。これにより、最も高価なリソースであるあなたを最大限に活用できます。プログラムを書いたり、後でデバッグしたり(理解が必要)します。パフォーマンスが十分でない場合は、measureボトルネックが存在する場所で、それらを軽減する方法を確認してください。非常にまれな場合にのみ、個々の(ソース)命令について心配する必要があります。パフォーマンスとは、最初の行で適切なアルゴリズムとデータ構造を選択し、慎重にプログラミングし、十分な速度のマシンを取得することです。優れたコンパイラーを使用すると、最新のコンパイラーが行うようなコードの再構築を見ると驚くでしょう。パフォーマンスのためにコードを再構築することは一種の最後の手段であり、コードはより複雑になり(したがってバグが増え)、変更が難しくなり、したがって全体的に高価になります。
私はかつて私の友人とこの議論をしました。彼は非常に素朴なサークルアルゴリズムを使用していましたが、私の場合(私の場合は円の1/8だけを計算する種類)よりも高速であると主張しました。最終的に、ifステートメントはsqrtに置き換えられ、なんとか高速になりました。おそらくFPUにsqrtが組み込まれているからでしょうか?
一部のCPU(X86など)は、このような分岐予測の待ち時間を回避するために、プログラミングレベルに分岐予測を提供します。
一部のコンパイラは、(GCCなど)これらを高レベルプログラミング言語(C/C++など)の拡張として公開します。
Linuxカーネルの likely()/ unlikely()マクロを参照してください-それらはどのように機能しますか?その利点は何ですか? 。