質問

できない、生命のもあり、正確に当てくださるとうれしい当日は、今までしょう。

このモジュールは"データ構造とアルゴリズム"と語ってくれました。う、日:

if 声明にはな 〕.【登録 〕.

あい、恐ろしい記憶ので、私は本当に申し訳ありませんが、私はgooglingのための時間も注目してきました。そのアイデア?

役に立ちましたか?

解決

非常に低いレベル(ハードウェア)では、はい、場合は高価です。理由を理解するには、パイプラインの仕組みを理解する必要があります。

現在実行される命令は、通常命令ポインタ(IP)またはプログラムカウンタ(PC)と呼ばれるものに格納されます。これらの用語は同義語ですが、アーキテクチャごとに異なる用語が使用されます。ほとんどの命令では、次の命令のPCは、現在のPCに現在の命令の長さを加えたものになります。ほとんどのRISCアーキテクチャでは、命令はすべて一定の長さであるため、PCを一定量だけ増やすことができます。 x86などのCISCアーキテクチャでは、命令を可変長にすることができるため、命令をデコードするロジックは、現在の命令が次の命令の位置を見つけるまでの時間を把握する必要があります。

branch 命令の場合、実行される次の命令は現在の命令の次の場所ではありません。ブランチはgotoです-次の命令がどこにあるかをプロセッサに伝えます。分岐は条件付きまたは無条件のいずれかにでき、ターゲットの場所は固定または計算できます。

条件付きと無条件の理解は簡単です-条件付き分岐は、特定の条件が成立する場合にのみ実行されます(1つの数値が別の数値と等しいかどうかなど)。分岐が行われない場合、制御は通常のように分岐後の次の命令に進みます。無条件分岐の場合、常に分岐が行われます。条件分岐は、 if ステートメントと、 for および while ループの制御テストに表示されます。無条件分岐は、無限ループ、関数呼び出し、関数リターン、 break および continue ステートメント、悪名高い goto ステートメントなどに現れます(これらリストは完全ではありません)。

別の重要な問題はブランチターゲットです。ほとんどのブランチには、固定されたブランチターゲットがあります。コンパイル時に修正されるコード内の特定の場所に移動します。これには、 if ステートメント、あらゆる種類のループ、通常の関数呼び出しなどが含まれます。 計算済みブランチは、実行時にブランチのターゲットを計算します。これには、 switch ステートメント(時々)、関数からの戻り、仮想関数呼び出し、および関数ポインター呼び出しが含まれます。

では、これがパフォーマンスにとって何を意味するのでしょうか?プロセッサは、分岐命令がパイプラインに表示されるのを確認したら、パイプラインを埋め続ける方法を見つける必要があります。プログラムストリーム内の分岐の後にどの命令が来るかを把握するには、2つのことを知る必要があります。(1)分岐が行われるかどうか、および(2)分岐のターゲット。これを理解することは、分岐予測と呼ばれ、難しい問題です。プロセッサが正しく推測した場合、プログラムは最高速度で続行します。代わりに、プロセッサが誤って推測する場合は、間違ったものを計算するのに少し時間を費やしただけです。パイプラインをフラッシュし、正しい実行パスからの命令でリロードする必要があります。結論:パフォーマンスが大幅に低下。

したがって、ステートメントが高価な理由は、ブランチの予測ミスが原因です。これは最下位レベルのみです。高レベルのコードを記述している場合、これらの詳細についてまったく心配する必要はありません。 Cまたはアセンブリで非常にパフォーマンスが重要なコードを書いている場合にのみ、これに注意する必要があります。その場合、さらにいくつかの命令が必要な場合でも、分岐のないコードを記述する方が分岐するコードよりも優れていることがよくあります。 abs() min()、およびのようなものを計算するために実行できるいくつかのクールなビット調整のトリックがあります。

他のヒント

"高価な"特に" if "との関係を持つ非常に相対的な用語です。条件のコストも考慮する必要があるため、ステートメント。これは、いくつかの短いcpu命令から、リモートデータベースを呼び出す関数の結果のテストまで、さまざまです。

心配しません。組み込みプログラミングを行っているのでなければ、おそらく" if "のコストを気にする必要はありません。まったく。ほとんどのプログラマーにとって、アプリのパフォーマンスを左右する要素は決してではありません。

ブランチ、特にRISCアーキテクチャのマイクロプロセッサでは、最も高価な命令の一部です。これは、多くのアーキテクチャで、コンパイラが実行パスが最も可能性が高いと予測し、それらの命令を実行可能ファイルに次に配置するためです。したがって、分岐が発生すると、CPUキャッシュに既に存在します。ブランチが逆の場合、メインメモリに戻って新しい命令をフェッチする必要があります。これはかなり高価です。多くのRISCアーキテクチャでは、分岐を除くすべての命令は1サイクルです(多くの場合2サイクルです)。ここでは大きなコストについて話していないので、心配する必要はありません。また、コンパイラは99%の時間をかけて最適化を行います:) EPICアーキテクチャ(Itaniumが例です)で本当に素晴らしいことの1つは、ブランチの両側からの命令をキャッシュ(および処理開始)することです。その後、ブランチの結果がわかったら、不要なセットを破棄します。これにより、予期しないパスに沿って分岐する場合に、典型的なアーキテクチャの余分なメモリアクセスが節約されます。

セルのパフォーマンスに関する記事ブランチの削除によるパフォーマンスの向上をご覧ください。 。別の楽しいものは、リアルタイムコリジョン検出ブログのブランチレス選択に関するこの投稿です。 >

この質問への回答として既に投稿された優れた回答に加えて、「if」がステートメントは高価な低レベル操作と見なされ、スクリプト言語やビジネスロジックレイヤー(言語に関係なく)などの高レベル環境でブランチフリープログラミング手法を利用しようとすると、とんでもなく不適切になる可能性があります。

ほとんどの場合、プログラムは明確にするために最初に記述し、次にパフォーマンスのために最適化する必要があります。パフォーマンスが最重要である多くの問題領域がありますが、単純な事実は、ほとんどの開発者がレンダリングエンジンのコアの奥深くで使用するモジュールや、何週間も続く高性能流体力学シミュレーションを書いていないことです。ソリューションが「適切に動作する」ことを最優先する場合最後に考えることは、コード内の条件ステートメントのオーバーヘッドを節約できるかどうかです。

最低レベルの if の構成(特定の if のアプリ固有の前提条件をすべて計算した後):

  • いくつかのテスト指示
  • テストが成功した場合はコード内のある場所にジャンプし、そうでない場合は次へ進みます。

それに関連する費用:

  • 低レベルの比較-通常1 CPUオペレーション、超安価
  • 潜在的なジャンプ-高価になる可能性があります

ジャンプが高価な理由:

  • CPUによってキャッシュされていないことが判明した場合、メモリ内の任意の場所にある任意のコードにジャンプできます。メインメモリにアクセスする必要があるため、問題が発生します。
  • 最新のCPUは分岐の前処理を行います。彼らは成功するかどうかを推測し、パイプラインで先にコードを実行しようとするので、スピードアップします。予測が失敗した場合、パイプラインによって先に行われたすべての計算を無効にする必要があります。それはまた高価な操作です

要約すると:

  • 費用がかかる可能性がある場合、本当に本当に本当にパフォーマンスが気になる場合。
  • リアルタイムレイトレーサーまたは生物学的シミュレーションなどを作成している場合にのみ、それを気にする必要があります。現実の世界のほとんどでそれを気にする理由はありません。

それ自体自体が遅くない場合。スローネスは常に相対的なものです。あなたが「オーバーヘッド」を感じたことのない私の人生に賭けています。 ifステートメントの。高性能なコードを作成する場合は、とにかく分岐を避けたいかもしれません。 if が遅くなるのは、プロセッサが何らかのヒューリスティックな方法などに基づいて if の後にコードをプリロードしていることです。また、マシンコードの if 分岐命令の直後にパイプラインがコードを実行するのを停止します。これは、プロセッサがまだどのパスを取るかわからないためです(パイプラインプロセッサでは、複数の命令がインターリーブされ、実行)。実行されたコードは逆に実行する必要があります(他のブランチが取得された場合。それは branch misdiction と呼ばれます)、または noop がそれらの場所で満たされるため、起こらない。

if が悪の場合、 switch も悪であり、&& || も。心配しないでください。

分岐によってCPU命令のプリフェッチが強制終了される可能性がありますか

最新のプロセッサには長い実行パイプラインがあります。つまり、複数の命令が同時にさまざまな段階で実行されます。次の命令が実行を開始したときに、ある命令の結果を常に把握しているとは限りません。条件付きジャンプ(if)に遭遇すると、パイプラインが空になるまで待ってから、命令ポインターの方向を知る必要がある場合があります。

私はそれを長い貨物列車と考えています。大量の貨物を直線で高速に運ぶことができますが、コーナーがひどく曲がります。

Pentium 4(プレスコット)には31ステージの有名な長いパイプラインがありました。

ウィキペディア

の詳細

これが参照していると想像できる唯一のことは、 if ステートメントが一般的に分岐する可能性があるという事実です。プロセッサアーキテクチャの仕様によっては、分岐によりパイプラインが停止したり、最適ではない状況が発生する可能性があります。

ただし、これは非常に状況に固有です。最新のプロセッサのほとんどは、分岐の悪影響を最小限に抑えるための分岐予測機能を備えています。別の例は、ARMアーキテクチャー(およびおそらく他のアーキテクチャー)が条件付きロジックを処理する方法です-ARMには命令レベルの条件付き実行があるため、単純な条件付きロジックは分岐しません-条件が満たされない場合、命令は単純にNOPとして実行されます。

すべてのこと-このことを心配する前にロジックを修正してください。誤ったコードは、可能な限り最適化されていません。

指摘されているように、多くの条件としできるように非常に遅いる現代。

しているようであり、条件付の支店がんば書いていかにコンパイラにあるをクリックします。は、悩みとか基本的な諸表まではほぼ常に悪いことだと思います。ができるかをコンパイラを確実についての最適化コンパイラです。)

CPUは深くパイプライン化されています。分岐命令(if / for / while / switch / etc)は、CPUが次にロードして実行する命令を実際に知らないことを意味します。

何をすべきかを待機している間にCPUがストールするか、CPUが推測します。古いCPUの場合、または推測が間違っている場合、パイプラインが停止して正しい命令をロードする必要があります。 CPUによっては、10〜20命令に相当するストールが発生する可能性があります。

最新のCPUは、適切な分岐予測を行い、複数のパスを同時に実行し、実際のパスのみを保持することで、これを回避しようとします。これは大いに役立ちますが、ここまでしかできません。

クラスで頑張ってください。

また、実生活でこれを心配する必要がある場合は、OSデザイン、リアルタイムグラフィックス、科学計算、または同様のCPUバウンドを実行している可能性があります。心配する前にプロファイルします。

また、ループ内は必ずしも非常に高価ではないことに注意してください。

最新のCPUは、if文を最初に訪れたときに、「if-body」が(または別の言い方をすると、ループ本体が複数回取得されることを前提としています)(*)。 2回目以降のアクセスでは、(CPU)が分岐履歴テーブルを調べて、条件が最後にどのようになったかを確認できます(本当ですか?それとも偽ですか?)。前回偽だった場合、投機的実行は「その他」に進みます。 ifの、またはループを超えて。

(*)ルールは実際には" 前方分岐はとられず、後方分岐はとられます"です。 if文では、条件がfalseと評価された場合(CPUがとにかく覚えている場合は、のみ [進む]ジャンプ(if-bodyの後のポイントへ)があります。分岐/ジャンプを行わないことを前提としています)が、ループ内では、ループの後の位置への順方向分岐(取られることはありません)と、反復時の逆方向分岐(取られること)があります。

これは、仮想関数または関数ポインタ呼び出しの呼び出しが多くの人が想定しているほど悪くない理由の1つでもあります( http://phresnel.org/blog/

明らかに非効率的ではない、最も明確でシンプルでクリーンな方法でプログラムを作成します。これにより、最も高価なリソースであるあなたを最大限に活用できます。プログラムを作成するか、後でデバッグする(理解が必要)か。パフォーマンスが十分でない場合は、ボトルネックのある場所を測定し、それらを軽減する方法を確認します。非常にまれな場合にのみ、個々の(ソース)命令について心配する必要があります。パフォーマンスとは、最初の行で適切なアルゴリズムとデータ構造を選択し、慎重にプログラミングし、十分な速度のマシンを取得することです。優れたコンパイラを使用すると、最新のコンパイラが行うようなコードの再構築を見ると驚くでしょう。パフォーマンスのためにコードを再構築することは一種の最後の手段であり、コードはより複雑になり(そのためバグが増えます)、変更が難しくなり、全体的に高価になります。

私は友人と一度この議論をしました。彼は非常に素朴なサークルアルゴリズムを使用していましたが、私の場合(私の場合は円の1/8しか計算しない種類)よりも高速であると主張しました。最終的に、ifステートメントはsqrtに置き換えられ、なんとか高速になりました。おそらく、FPUにsqrtが組み込まれているためですか?

一部のCPU(X86など)は、このような分岐予測レイテンシを回避するためにプログラミングレベルに分岐予測を提供します。

一部のコンパイラは、(GCCなどの)これらを高レベルのプログラミング言語(C / C ++など)の拡張として公開しています。

Linuxカーネルの likely()/ unlikely()マクロを参照する-方法彼らは働いていますか?彼らの利点は何ですか?

ALUの使用に関して最も高価ですか? CPUレジスタを使用して比較する値を保存し、ifステートメントが実行されるたびに値をフェッチして比較するのに時間がかかります。

したがって、それを最適化するには、ループを実行する前に1つの比較を行い、結果を変数として保存します。

不足している単語を解釈しようとしています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top