今天和同事讨论GPU如何实现求导(求偏导),过程中又学习了一下GPU的工作模式。然后发现了我以前的一个误解:我以前以为shader中动态分支会造成性能损失50%,其实这只是一个估计值。实际情况下,GPU性能最坏情况是无分支的1/8 (N卡GTX480以上为1/16),而最好情况也只是无分支的7/8 (N卡GTX480以上为15/16),各种情况取平均值大概是 1/2 。
首先补充一下GPU中分支语句的种类。GPU中一共有三种分支语句,第一种是 #if #else,由宏来实现,被称作静态分支,在编译期决定,没有性能损失;第二种是if else语句,但是if条件中仅有uniform变量和常量组成,在运行期相当于 #if #else,也没有运行期损失(但是驱动判断static uniform branch有轻微消耗);第三种就是真正的if else语句,也就是动态分支。
先举一个简单的例子,来说明去除动态分支的方法吧。

可以被优化成:

如果使用HLSL,则应用
step
函数取代GLSL中的sign
函数
对于代码:

则可以优化成:

通过这两个例子,大概可以看出来目的了。这种优化的实质,就是分支合并,手工计算了所以分支的情况,再只取有用的部分。这种优化只会在分支部分损耗加倍,不会造成老旧GPU那样整个shader性能折半的情况。
接下来列举一些常用的优化分支函数。
常用关系运算符优化

常用逻辑运算符优化

给浮点数求余数,HLSL应使用
fmod
函数;GLSL应使用modf
函数,且只有openGL 3.x/openGL ES 3.x 中才能使用