Dar*_*man 7 optimization shader branch hlsl
我有一个着色器,我想在顶点着色器中移动一半顶点.我试图从性能的角度来决定最好的方法,因为我们处理的是超过100,000个顶点,因此速度至关重要.我看了3种不同的方法:(伪代码,但足以给你这个想法.<complex formula>
我不能给出,但我可以说它涉及一个sin()
函数,以及一个函数调用(只是返回一个)数字,但仍然是函数调用),以及浮点数的一堆基本算术).
if (y < 0.5)
{
x += <complex formula>;
}
Run Code Online (Sandbox Code Playgroud)
这样做的优点<complex formula>
是只执行了一半的时间,但缺点是肯定会导致分支,实际上可能比公式慢.它是最具可读性的,但在这种情况下我们更关心速度而不是可读性.
x += step(y, 0.5) * <complex formula>;
Run Code Online (Sandbox Code Playgroud)
使用HLSL的step()函数(如果第一个参数更大则返回0,如果更少则返回1),可以消除分支,但现在<complex formula>
每次都调用它,并且其结果乘以0(因此浪费了精力)一半的时间.
x += (y < 0.5) ? <complex formula> : 0;
Run Code Online (Sandbox Code Playgroud)
这个我不知道.是否?:
会导致一个分支?如果不是,那么等式的两边都要进行评估,还是只考虑相关的一方?
最后的可能性是<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>;
Run Code Online (Sandbox Code Playgroud)
可能比其中任何一个都好,因为在任何地方都没有比较.(并且y
始终为0或1.)仍然执行<complex formula>
不必要的一半时间,但可能值得完全避免分支.
或许看看这个答案.
我的猜测(这是一个性能问题:测量它!)是你最好保留if
声明.
原因一:理论上(如果正确调用),着色器编译器应该足够聪明,以便step
在编译if
语句时在分支指令和类似函数之间做出最佳选择.改进它的唯一方法是剖析[1].请注意,它可能在此粒度级别依赖于硬件.
[1]或者如果您对如何布置数据有具体的了解,请继续阅读......
第二个原因是着色器单元的工作方式:如果单元中的一个片段或顶点对其他分支或顶点采用不同的分支,则着色器单元必须采用两个分支.但如果他们都采用相同的分支 - 另一个分支被忽略.因此,虽然它是单位的,而不是每个顶点 - 但仍然可以跳过昂贵的分支.
对于片段,着色器单元具有屏幕上的位置 - 这意味着您可以获得最佳性能,附近像素组都采用相同的分支(请参阅我的链接答案中的插图).说实话,我不知道如何将顶点分组为单位 - 但如果您的数据被适当地分组 - 您应该获得所需的性能优势.
最后:值得指出的是<complex formula>
- 如果你说你可以手动将它从你的HLSL中提升出来 - 它可能会被提升到基于CPU的预着色器中(至少在PC上,从内存Xbox 360开始)不支持这个,不知道PS3).您可以通过反编译着色器来检查这一点.如果它是你需要每次绘制一次计算的东西(而不是每顶点/片段),那么在CPU上执行它可能是最好的.