Do conditional statements slow down shaders?
我想知道着色器(顶点/片段/像素...)内部的" if语句"是否真的减慢了着色器的性能。例如:
最好使用这个:
1 2 | vec3 output; output = input*enable + input2*(1-enable); |
而不是使用这个:
1 2 3 4 5 6 7 8 9 | 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:
1 2 3 4 5 6 7 8 | if(enable == 1) //never happens { output = vec4(0,0,0,0); } else //always happens { output = calcPhong(normal, lightDir); } |
我认为这里在着色器内部有一个分支,可降低着色器的速度。那是对的吗?
制作2个不同的着色器(例如一个用于其他,另一个用于if部分)是否更有意义?
关于着色器,甚至可能导致
好的。
单独的着色器调用通常并行执行,同时执行相同的指令。他们只是在不同的输入值集上执行它们。他们共用制服,但内部寄存器不同。一组全部执行相同操作序列的着色器的一个术语是"波前"。
好的。
任何形式的条件分支的潜在问题是,它可能将所有问题搞砸了。它导致波前内的不同调用必须执行不同的代码序列。这是一个非常昂贵的过程,因此必须创建一个新的波前,将数据复制到该波前,等等。
好的。
除非...不是。
好的。
例如,如果条件是波阵面中每次调用所采用的条件,则不需要运行时差异。这样,
好的。
因此,假设您有一个条件分支,并假设波前中的所有调用都采用相同的分支。在这种情况下,表达式的性质存在三种可能性:
好的。
好的。
不同的硬件可以处理不同的分支类型而不会产生分歧。
好的。
同样,即使条件是由不同的波前采取的,编译器也可以重组代码以不需要实际的分支。您给出了一个很好的示例:
好的。
几乎所有硬件都可以处理
好的。
由于这是非常依赖于硬件的,因此...取决于硬件。但是,可以看到某些特定的硬件时代:
好的。
D3D10之前的台式机
那里有点荒野。 NVIDIA针对此类硬件的编译器以检测此类情况而臭名昭著,并在您更换影响此类条件的制服时实际上重新编译了着色器。
好的。
通常,这个时代大约是80%的"从不使用
好的。
您可以期望静态分支的优化。您可以希望,静态统一分支不会导致任何其他问题(尽管NVIDIA认为重新编译比执行速度更快的事实至少在他们的硬件上不太可能实现)。但是,即使所有调用都采用同一分支,动态分支也会使您付出一些代价。
好的。
这个时代的编译器会尽最大努力优化着色器,以便简单地执行简单条件。例如,您的
好的。
台式机,D3D10后
这个时代的硬件通常能够以很小的速度处理静态统一的分支语句。对于动态分支,您可能会遇到减速,也可能不会遇到减速。
好的。
台式机,D3D11 +
可以肯定地保证了这个时代的硬件能够在几乎没有性能问题的情况下动态地处理统一条件。实际上,它甚至不必是动态统一的。只要同一波前的所有调用采用相同的路径,就不会看到任何明显的性能损失。
好的。
请注意,上一个时期的某些硬件也可能会这样做。但这几乎可以肯定是真的。
好的。
移动版,ES 2.0
欢迎回到荒野的西部。尽管与Pre-D3D10台式机不同,这主要是由于ES 2.0口径硬件的巨大差异。可以处理ES 2.0的东西太多了,它们彼此之间的工作方式大不相同。
好的。
静态分支可能会得到优化。但是,是否从静态统一分支中获得良好的性能取决于硬件。
好的。
移动版,ES 3.0+
这里的硬件比ES 2.0更成熟,功能更强大。这样,您可以期望静态统一分支能够合理执行。而且某些硬件可能可以像现代台式机硬件一样处理动态分支。
好的。
好。
它高度依赖于硬件和条件。
如果您的条件是统一的:不要打扰,让编译器处理它。
如果您的条件是动态的(例如从属性计算出的值或从纹理或某些东西中获取的值),则情况会更加复杂。
对于后一种情况,您几乎必须进行测试和基准测试,因为这取决于每个分支中代码的复杂性以及分支决策的"一致性"。
例如,如果分支之一占案件的99%,并丢弃该片段,那么您很可能希望保留条件。 但是在上面的简单示例中,OTOH如果
除非您拥有上述类似的明确案例,或者除非您针对一种固定的已知体系结构进行了优化,否则编译器的帮助可能会更好。