web-dev-qa-db-ja.com

シェーダーの最適化:三項演算子は分岐と同等ですか?

いくつかの頂点を条件付きでドロップしたい頂点シェーダーに取り組んでいます:

float visible = texture(VisibleTexture, index).x;
if (visible > threshold)
    gl_Vertex.z = 9999; // send out of frustum

隣接するデータ間に共通性がほとんどない場合、ブランチはパフォーマンスを低下させることを私は知っています。この場合、他のすべての頂点が異なる「可視」値を取得する可能性があり、これはローカルシェーダーコアクラスターのパフォーマンスに悪影響を及ぼします(私の理解から)。

私の質問へ:(読みやすさの問題に関係なく)三項演算子の方が優れていますか?

float visible = texture(VisibleTexture, index).x;
gl_Vertex.z = (visible > threshold) ? 9999 : gl_Vertex.z;

そうでない場合、それを計算に変換する価値はありますか?

float visible = texture(VisibleTexture, index).x;
visible = sign(visible - threshold) * .5 + .5; // 1=visible, 0=invisible
gl_Vertex.z += 9999 * visible; // original value only for visible

ジオメトリシェーダーに依存せずに頂点をドロップするさらに良い方法はありますか?

助けてくれてありがとう!

28
sharoz

三項演算子は、ifステートメントの単なる構文糖衣です。それらは同じです。

Ifステートメントの内部にさらに書き込む必要がある場合は、ここで実行できる最適化があるかもしれませんが、どちらのブランチの内部もほとんどないため、実際に最適化するものはありません。

多くの場合、デフォルトでは分岐は使用されません。

あなたの場合、三項演算子(またはifステートメント)は、おそらく最初に条件の両側を評価してから、条件によって満たされていないブランチを破棄しています。

分岐を使用するには、シェーダーコードで分岐コンパイラフラグを設定して、GPUに実際に分岐を試みるように指示するアセンブリを生成する必要があります(GPUが分岐をサポートしている場合)。その場合、GPUは、分岐予測子が、事前定義された数のコアが分岐の1つを取ると言った場合にのみ分岐を試みます。

マイレージは、コンパイラやGPUによって異なる場合があります。

8
Olhovsky

この数学的解決策は、条件文の置き換えに使用できます。これは、OpenCLでもbitselect(condition, falsereturnvalue, truereturnvalue);として実装されています。

int a = in0[i], b = in1[i];
int cmp = a < b; //if TRUE, cmp has all bits 1, if FALSE all bits 0
// & bitwise AND
// | bitwise OR
// ~ flips all bits
out[i] = (a&cmp) | (b&~cmp); //a when TRUE and b when FALSE

ただし、これをあなたの状況に実装するかどうかはわかりません。あなたのコードを完全に理解したかどうかはわかりませんが、この回答を提供することが役立つことを願っています。

10
Mnescat

実際、これは使用するシェーダー言語によって異なります。

  • HLSLとCgでは、三項演算子が分岐につながることはありません。代わりに、両方の可能な結果が常に評価され、使用されていない結果は破棄されます。 HLSLドキュメント を引用するには:

    &&、||、および?:の短絡評価とは異なり、Cでは、HLSL式はベクトル演算であるため、評価を短絡することはありません。式のすべての側面が常に評価されます。

    Cgの場合も状況は似ており、ここでも3項条件演算子はベクトル演算子です。 ( ドキュメント ):

    Cとは異なり、第2オペランドと第3オペランドの式の副作用は、条件に関係なく常に実行されます。

  • ESSLおよびGLSLでは、三項演算子は常に分岐につながります。これはベクトル演算子ではないため、条件はブール値に評価される必要があります。 GLSL仕様 を参照してください:

    3つの式(exp1?exp2:exp3)で動作します。この演算子は最初の式を評価します。これは、スカラーブール値になる必要があります。結果がtrueの場合、2番目の式を評価することを選択し、そうでない場合は、3番目の式を評価することを選択します。 2番目と3番目の式の1つだけが評価されます。

    ESSLのソース

違いの図は、たとえば 三項演算子のKhronos WebGLテストサイト で入手できます。

7
soulsource

答えは3つのことに依存します:

  1. コンパイラとそれが実行する最適化の種類
  2. アーキテクチャと言語
  3. 三項演算子を使用している正確な状況。

この例を考えてみましょう。

int a = condition ? 100 : 0;

この場合、一般的なアーキテクチャの一般的なコンパイラは、ブール値が整数として表されていると仮定して、分岐を削除できる可能性があります。コードは次のように翻訳できます

int a = condition * 100

同等のif条件を使用すると、同じ種類の最適化が可能になる場合があります。

int a = 0;

if (condition) {
    a = 100;
}

それはすべて、コンパイラーによって実行される特定の最適化に依存します。

一般的に言って、私のアドバイスは次のとおりです。三項演算子を使用できる場合は、それを使用することをお勧めします。コンパイラによって最適化される可能性が高くなります。また、より宣言的なスタイルのコードになります。

1
HRJ

私の知る限り、これを最適化する方法はありません。コンパイラは引き続きブランチを強制されます。各提案は機能的に同等です。

0
jakev