web-dev-qa-db-ja.com

条件付きステートメントはシェーダーを遅くしますか?

シェーダー(頂点/フラグメント/ピクセル...)内の「ifステートメント」がシェーダーのパフォーマンスを本当に低下させているかどうかを知りたいです。例えば:

これを使用する方が良いですか:

vec3 output;
output = input*enable + input2*(1-enable);

これを使用する代わりに:

vec3 output;
if(enable == 1)
{
    output = input;
}
else
{
    output = input2;
}

別のフォーラムでそれについての話がありました(2013): http://answers.unity3d.com/questions/442688/shader-if-else-performance.html ここでみんなが言っている、 Ifステートメントは、シェーダーのパフォーマンスにとって本当に悪いです。

また、ここで彼らはif/elseステートメント(2012)の中にどれくらいあるかについて話している: https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-( -)

ハードウェアまたはシェーダーコンパイラの方が優れている可能性があり、何らかの形でこの(既存ではないかもしれない)パフォーマンスの問題を修正しています。

編集:

この場合、何が有効なのは均一変数であり、常に0に設定されていると言えます。

if(enable == 1) //never happens
{
    output = vec4(0,0,0,0);
}
else  //always happens
{
    output = calcPhong(normal, lightDir);
}

ここでは、シェーダーの内部にシェーダーを遅くするブランチがあると思います。あれは正しいですか?

Elseパーツとifパーツ用の2つの異なるシェーダーを作成する方が理にかなっていますか?

47
Thomas

シェーダーに関して、ifステートメントのパフォーマンスの問題を引き起こす可能性さえありますか?それは、シェーダーの実行方法と、GPUが大規模なコンピューティングパフォーマンスを取得する場所に関係しています。

通常、別々のシェーダー呼び出しは並行して実行され、同じ命令を同時に実行します。彼らは単に入力値の異なるセットでそれらを実行しています。それらはユニフォームを共有しますが、内部レジスタは異なります。同じ一連の操作を実行するすべてのシェーダーのグループの1つの用語は「ウェーブフロント」です。

あらゆる形態の条件分岐の潜在的な問題は、それがすべてを台無しにする可能性があることです。これにより、異なるコードシーケンスを実行する必要があるウェーブフロント内の異なる呼び出しが発生します。これは非常に高価なプロセスであり、新しい波面を作成したり、データにコピーしたりする必要があります。

しない限り...そうではありません。

たとえば、条件が、波面でのevery呼び出しによって取られる条件である場合、実行時の相違は必要ありません。そのため、ifのコストは、条件をチェックするコストにすぎません。

したがって、条件分岐があるとします。また、波面内のすべての呼び出しが同じ分岐を取ると仮定しましょう。その条件での式の性質には3つの可能性があります。

  • コンパイル時の静的。条件式は、コンパイル時の定数に完全に基づいています。そのような場合、コードを見ると、どの分岐が行われるかがわかります。ほとんどのコンパイラは、これを基本的な最適化の一部として処理します。
  • 静的に均一な分岐。条件は、コンパイル時に定数であることがわかっているもの(具体的には定数とuniform値)を含む式に基づいています。ただし、式のvalueは、コンパイル時にはわかりません。したがって、コンパイラは、このifによって波面が決して破壊されないことを静的に確信できますが、コンパイラはどの分岐が行われるかを知ることができません。
  • 動的分岐。条件式には、定数とユニフォーム以外の用語が含まれています。ここで、コンパイラは、波面が分割されるかどうかをアプリオリに伝えることはできません。それが発生する必要があるかどうかは、条件式の実行時評価に依存します。

異なるハードウェアは、分岐することなく異なるタイプの分岐を処理できます。

また、条件が異なる波面によってとられた場合でも、コンパイラは実際の分岐を必要としないようにコードを再構築できます。良い例を挙げました:output = input*enable + input2*(1-enable);は機能的にifステートメントと同等です。コンパイラは、変数を設定するためにifが使用されていることを検出し、したがって両方を実行できます。これは、分岐の本体が小さい動的条件の場合に頻繁に行われます。

ほとんどすべてのハードウェアは、分岐することなく_var = bool ? val1 : val2_を処理できます。これは2002年に可能な方法でした。

これはハードウェアに非常に依存しているため、...ハードウェアに依存します。ただし、確認できるハードウェアには特定のエポックがあります。

デスクトップ、Pre-D3D10

そこはちょっと西部です。そのようなハードウェア用のNVIDIAのコンパイラは、そのような状態を検出し、実際にシェーダーを再コンパイルすることで有名でした。

一般に、この時代は「ifステートメントを使用しない」の約80%が由来する場所です。しかし、ここでも、必ずしもそうとは限りません。

静的分岐の最適化が期待できます。 hopeを使用して、静的に均一な分岐が追加のスローダウンを引き起こさないようにすることができます(ただし、NVIDIAは、再コンパイルが実行よりも高速であると考えたため、少なくとも彼らのハードウェアに対して)。ただし、すべての呼び出しが同じブランチを使用する場合でも、動的な分岐にはコストがかかります。

この時代のコンパイラは、シェーダーを最適化して、単純な条件を簡単に実行できるように最善を尽くしています。たとえば、output = input*enable + input2*(1-enable);は、まともなコンパイラが同等のifステートメントから生成できるものです。

デスクトップ、ポストD3D10

この時代のハードウェアは一般に、ほとんどスローダウンせずに静的に均一な分岐ステートメントを処理できます。動的分岐の場合、スローダウンが発生する場合と発生しない場合があります。

デスクトップ、D3D11 +

この時代のハードウェアは、パフォーマンスの問題がほとんどない状態で 動的に均一 の条件を処理できることがほぼ保証されています。実際、動的に均一である必要はありません。同じウェーブフロント内のすべての呼び出しが同じパスをとる限り、パフォーマンスが大幅に低下することはありません。

以前のエポックの一部のハードウェアもおそらくこれを行うことができることに注意してください。しかし、これは真実であることがほぼ確実なものです。

モバイル、ES 2.0

野生の西へようこそ。 Pre-D3D10デスクトップとは異なりますが、これは主にES 2.0対応ハードウェアの大きな違いによるものです。 ES 2.0を処理できるものは非常に大量にあり、それらはすべて互いに非常に異なる動作をします。

静的分岐はおそらく最適化されます。しかし、静的に均一な分岐から良好なパフォーマンスを得るかどうかは、ハードウェアに大きく依存します。

モバイル、ES 3.0+

ここのハードウェアは、ES 2.0よりもかなり成熟しており、機能します。そのため、静的に均一なブランチが適切に実行されることが期待できます。また、一部のハードウェアは、おそらく最新のデスクトップハードウェアのように動的ブランチを処理できます。

99
Nicol Bolas

ハードウェアと条件に大きく依存します。

条件が統一されている場合:気にしないで、コンパイラに対処させてください。条件が動的なもの(属性から計算された値やテクスチャから取得された値など)である場合、より複雑です。

後者の場合、各ブランチのコードの複雑さとブランチの決定の「一貫性」に依存するため、テストとベンチマークを行う必要があります。

たとえば、分岐の1つがケースの99%を占めてフラグメントを破棄する場合、ほとんどの場合、条件を保持する必要があります。ただし、上記の簡単な例のOTOHでは、enableが何らかの動的条件である場合、算術選択の方が優れている可能性があります。

上記のような明確なケースを持たない限り、または既知の1つの固定アーキテクチャに最適化する場合を除いて、おそらくコンパイラーがそれを理解する方がよいでしょう。

6
246tNt