Optimization 分支避免

Optimization 分支避免,optimization,branch,shader,hlsl,Optimization,Branch,Shader,Hlsl,我有一个着色器,我想在其中移动顶点着色器中的一半顶点。我正试图从性能的角度来决定实现这一点的最佳方法,因为我们要处理的是超过100000个垂直,所以速度至关重要。我已经研究了3种不同的方法:(伪代码,但足够给你一个想法。我不能给出,但我可以说它涉及一个sin()函数,以及一个函数调用(只返回一个数字,但仍然是一个函数调用),以及一系列关于浮点数的基本算法) 很可能比他们中的任何一个都好,因为在任何地方都没有进行比较。(并且y始终为0或1。)仍然有一半的时间不必要地执行,但完全避免分支可能是值得的

我有一个着色器,我想在其中移动顶点着色器中的一半顶点。我正试图从性能的角度来决定实现这一点的最佳方法,因为我们要处理的是超过100000个垂直,所以速度至关重要。我已经研究了3种不同的方法:(伪代码,但足够给你一个想法。
我不能给出,但我可以说它涉及一个
sin()
函数,以及一个函数调用(只返回一个数字,但仍然是一个函数调用),以及一系列关于浮点数的基本算法)

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

请看

我的猜测(这是一个性能问题:衡量它!)是最好不要使用
if
语句

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

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

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

对于片段,着色器单元具有屏幕上的局部性—这意味着您可以在相邻像素组都采用相同分支时获得最佳性能(请参见我的示例中的插图)。老实说,我不知道顶点是如何分组到单元中的——但是如果数据分组正确的话——您应该会获得期望的性能优势


最后:值得指出的是,你的
——如果你说你可以手动将其从HLSL中取出——它很可能会被提升到基于CPU的预着色器中(至少在PC上,Xbox 360不支持这一点,不知道PS3)。可以通过反编译着色器来检查这一点。如果每次绘制只需要计算一次(而不是每个顶点/片段),那么最好在CPU上进行计算。

我厌倦了忽略我的条件,所以我只做了另一个内核,并在c执行中进行了重写。
如果您需要始终保持准确,我建议使用此修复方法。

方法取决于目标硬件。您可以比较这些变体的装配代码(例如,
RenderMonkey
可以分析Radeon卡的性能)。此外,顶点着色器中是否存在瓶颈?可能所有变体都会给出相同的结果:)某些核心上的着色器必须以锁定的方式执行,因此无论在任何情况下,
复杂公式
都会得到计算。此外,如果
y
可以作为网格的函数进行计算,可能您只需分割网格或场景几何体并运行两个不同的着色器。目标平台是XBox360,PS3和PC。(PC构建仅用于测试,因此不太重要。通常,它使用与PS3相同的代码。)了解
y
始终是0或1,两者之间没有任何值可能会有所帮助。(这是四边形的纹理坐标。)我想
step()
函数可以简单地替换为
(1.0-y)
,并具有相同的效果。仍然会导致公式的计算量是严格必要的两倍…哦,根据上面的说明,两个不同的着色器不是一个选项,因为每个多边形都会超过阈值。它只是渲染了很多四边形。将其视为粒子效应。不完全是这样,但这是我认为在不违反NDA的情况下可以安全获得的最接近的。幸运的是,我这里没有处理frag着色器,所以这不是一个问题。对于顶点,它可能遵循00110011模式-每个四边形的顶部2个顶点和底部2个顶点。我很确定所有四边形都是在一次绘制调用中绘制的,但是顶点的移动是基于每个四边形的。老实说,PC上发生的事情并不重要,因为这不是可交付的。这里的其他人说,
if
是不好的,应该不惜一切代价避免,但我想在我们得到这个评测之前,我无法确定。它很可能会转移到CPU上,但这有点棘手…XBox:Control(无顶点移动):830K GPU周期的数字。方法1(
if
):834K循环。方法2(
step()
):836K个周期。方法3(
?:
):835K循环。方法4(
1-y
):844K循环。我真的很惊讶最后一个是最慢的,因为它是唯一一个没有分支的。但您对
if
的看法是正确的,至少在本例中是这样。不过,我听说PS3将是另一个故事。我们看看会怎么样。哎呀,等等。我用不同的相机角度(一个远镜头显示更多的四边形,但远得多)重做了测试,结果颠倒了过来。对照组:566K,方法1:595K,方法2:590K,方法3:591K,方法4:611K
1-y
仍然是最糟糕的,但其他3个完全相反。这可能是一个更典型的视角,因此我必须用
step()
作为本例的最佳解决方案进行投票,尽管
?:
在这两种情况下都非常接近。PS3还没有定论。不知道这有什么关系?还有,这个问题是3年前的2份工作了,所以我甚至连相关的代码都没有了…只是把它放在那里。你是说你把逻辑移到了一个公司
if (y < 0.5)
{
    x += <complex formula>;
}
x += step(y, 0.5) * <complex formula>;
x += (y < 0.5) ? <complex formula> : 0;
x += (1.0 - y) * <complex formula>;