让我们看一个简化的GLSL函数示例:
以下是示例代码:
void foo() {
vec2 localData[16];
// ...
int i = ... // somehow dependent on dynamic data (not known at compile time)
localData[i] = x; // THE IMPORTANT LINE
}
它将一些值x
写入本地数组中动态确定的索引。现在,将行localData [i] = x;
替换为
for( int j = 0; j < 16; ++j )
if( i == j )
localData[j] = x;
使用循环写入代码可以使执行时间几乎减半,测试示例(不同的着色器)中执行时间几乎减少了一半,而且比这个写入操作更复杂。
例如:在一个无序透明着色器中,除了获取16个纹理之外,还有其他事情要做,在直接写入时计时为39ms
,在循环写入时计时为23ms
。没有其他改变!
测试硬件是GTX1080。由glGetProgramBinary
返回的汇编仍然过于高级。第一个案例中只包含一行,而第二个案例中包含一个循环+if语句围绕着相同的行。
- 为什么会出现这种性能问题?
- 所有供应商都是如此吗?
猜测:localData
存储在8个vec4寄存器中(汇编并未说明)。进一步假设,寄存器不能用索引进行寻址。如果两者都是真的,则最终二进制必须使用某种分支结构。循环变量可能会展开,并产生类似于switch
的模式,这样会更快。但这对所有供应商来说都是普遍的吗?为什么编译器不能使用for
循环的任何结果作为此类写入的默认值?