在写shader的时候,其实一些写法对于其执行影响非常大,而且由于gpu和cpu在架构上的不同,代码的优化思想也不一样,最近一直在写几个shader,为了性能问题,查阅了很多资料,把一些tips总结下来。
首先要树立几个思想:
1.gpu是SIMD的架构,即单指令多数据流架构,即在gpu上同时执行n个数据和执行1个数据的效率是一样的,我们要
尽量的把并行的计算搬到gpu上
2.gpu是以向量计算为基础设计的,也就是说在gpu上执行一个向量乘法和执行一个float的乘法的效率是一样的,
并不向cpu那样要多执行几次
所以
1.尽量把一些计算合并成向量计算,记住一个向量计算和一个float计算那样快!
比如
float x,y;
x = x * a;
y = y * b;
不如写成 float2 v = float2(x,y);
v = v*float2(a,b);
因为前一种写法是两次乘法计算,而后一种只要1次
2.不要在gpu里面用分支或者条件判断这种语句,尽管大多数gpu 的shader支持这种语法,但是多数gpu里面的
这种控制语句涉及到一些同步等消耗的操作,其实大多数这种语句都可以用数值的方式替代。
比如 你想写
float4 a;
if(b > 1)
{
a.a=1;
}
else
{
a.a =0.5;
}
可以改写成
float4 a;
float tmp = step(b,1);
a = tmp * 0.5 + (1-tmp);
if else可以被step出来的0 或1的乘法代替又比如
float4 a;
if(b && c || d && e)
{
a.a = 1;
}
else
{
a.a = 0.5;
}
可以写成
float4 a;
float tmp = step(1, (float)b*(float)c + float(d)*float(e));
a = tmp + (1-tmp) * 0.5;
&& 我们用转换到float后的乘法代替,||可以被判断加法step 1代替
而这种操作,尤其是shader内置的函数比条件判断和分支的效率要高很多,别忘了,GPU纯粹是为了计算的,而不是
做判断
3.尽量使用shader为我们提供的内置函数,这些内置的函数比我们想象的要快很多,往往应用了某些gpu的特殊
特性。
比如要比较a和b谁大用max(a,b),还有例如上面反复用的step,虽然你可以写用(float)(a>=1)来替换step(1,a),
但是这还是没有内置函数更快的,包括常用的saturate()把一个数归到0-1,总之一句话,如果能用一个内置函数替换
你的某些代码,就尽量替换。而且这些内置函数基本上都是支持对向量操作的,所以如果用step(a,fixed3(1,2,3))其
实只是一条指令,但是却可以同时返回用a同1 2 3分别比较的结果。
4.使用swizzle是非常快的,例float4 a = float4(1,1,1,1),用a.wz = float2(2,3)要比 a.w=3; a.z=2要高效很多
5.使用合适的数据类型,大部分gpu支持f的数值类型基本上分为fixed half float,分别是12位的定点数,16位的浮
点数以及32位的浮点数,尽可能的选择位数更少的数据类型来加快操作