HLSL分支避免

时间:2012-09-17 13:47:55

标签: optimization branch shader hlsl

我有一个着色器,我想在顶点着色器中移动一半顶点。我试图从性能的角度来决定最好的方法,因为我们处理的是超过100,000个顶点,因此速度至关重要。我已经看了3种不同的方法:(伪代码,但足以给你一个想法。<complex formula>我无法给出,但我可以说它涉及sin()函数,以及一个函数调用(只返回一个数字,但仍然是一个函数调用),以及一堆关于浮点数的基本算法。)

if (y < 0.5)
{
    x += <complex formula>;
}

这样做的好处是<complex formula>只执行了一半的时间,但缺点是肯定会导致分支,这实际上可能比公式慢。它是最具可读性的,但在这种情况下我们更关心速度而不是可读性。

x += step(y, 0.5) * <complex formula>;

使用HLSL的step()函数(如果第一个参数更大则返回0,如果更少则返回1),可以消除分支,但现在每次调用<complex formula>,其结果正在一半时间乘以0(因此浪费精力)。

x += (y < 0.5) ? <complex formula> : 0;

这个我不知道。 ?:导致分支吗?如果不是,那么等式的两边都要进行评估,还是只评估相关的一面?

最后的可能性是<complex formula>可以卸载回CPU而不是GPU,但我担心计算sin()和其他操作会慢一点,这可能会导致净损失。此外,这意味着必须将另外一个数字传递给着色器,这也可能导致开销。任何人都可以了解哪种方法最佳?


附录:

根据http://msdn.microsoft.com/en-us/library/windows/desktop/bb509665%28v=vs.85%29.aspx

step()函数在内部使用?:,所以它可能不比我的第三个解决方案好,并且可能更糟,因为<complex formula>每次都被调用,而可能只能用直线?:调用一半时间。 (目前还没有人回答这部分问题。)虽然避免使用和使用:

x += (1.0 - y) * <complex formula>;

可能比其中任何一个都好,因为在任何地方都没有比较。 (并且y始终为0或1.)仍然不必要地执行<complex formula>一半的时间,但可能值得完全避免分支。

2 个答案:

答案 0 :(得分:7)

或许请看this answer

我的猜测(这是一个性能问题:衡量它!)是你最好保留if声明。

原因第一:理论上(如果正确调用),着色器编译器应该足够聪明,以便在分支指令和类似于step函数的东西之间做出最佳选择,当它编译你的if声明。改进它的唯一方法是分析 [1] 。请注意,它可能在此粒度级别依赖于硬件。

[1]或者,如果您对如何布置数据有具体的了解,请继续阅读......

第二个原因是着色器单元的工作方式:如果单元中的一个片段或顶点对其他分支或顶点采用不同的分支,则着色器单元必须采用两个分支。但如果他们都采用相同的分支 - 另一个分支被忽略。因此,虽然它是每单位而不是每顶点 - 但仍然可以跳过昂贵的分支。

对于片段,着色器单元具有屏幕上的位置 - 这意味着您可以获得最佳性能,所有像素组都采用相同的分支(请参阅我的linked answer中的插图)。说实话,我不知道如何将顶点分组为单位 - 但如果您的数据被适当地分组 - 您应该获得所需的性能优势。

最后:值得指出的是你的<complex formula> - 如果你说你可以手动将它从HLSL中提起 - 它可能会被提升到基于CPU的预着色器中(无论如何)至少,从内存Xbox 360不支持这一点,不知道PS3)。您可以通过反编译着色器来检查这一点。如果它是你需要计算一次每次绘制(而不是每顶点/片段)的东西,那么 对于在CPU上执行它的性能来说可能。

答案 1 :(得分:0)

我厌倦了我的条件被忽略所以我只是做了另一个内核并在c执行中做了覆盖。 如果你需要它一直准确我建议这个修复。