Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have heard that if statements should be avoid in shaders, because both parts of the statements will be execute, and than the wrong will be dropped (which harms the performance).

It's still a problem in DirectX 10? Somebody told me, that in it only the right branch will be execute.

For the illustration I have the code:

float y1 = 5; float y2 = 6; float b1 = 2; float b2 = 3;

if(x>0.5){
    x = 10 * y1 + b1;
}else{
    x = 10 * y2 + b2;
}

Is there an other way to make it faster?

If so, how do it?

Both branches looks similar, the only difference is the values of "constants" (y1, y2, b1, b2 are the same for all pixels in Pixel Shader).

share|improve this question
    
Honestly, that's very premature optimization, just don't change them until you benchmarked your code and are 100% that shader is a bottleneck. –  pwny Dec 11 '12 at 22:55

4 Answers 4

up vote 10 down vote accepted

Many rules for micro-optimising shaders are the same as for traditional CPUs with vector extensions. Here are a few hints:

  • there are built-in test functions (test, lerp/mix)
  • adding two vectors has the same cost as adding two floats
  • swizzling is free

It is true that branches are cheaper on modern hardware than they used to be, but it is still better to avoid them if possible. By using swizzling and test functions you can rewrite your shader without tests:

/* y1, y2, b1, b2 */
float4 constants = float4(5, 6, 2, 3);

float2 tmp = 10 * constants.xy + constants.zw;
x = lerp(tmp[1], tmp[0], step(x, 0.5));

Using step and lerp is a very common idiom for choosing between two values.

share|improve this answer

If you're not already having performance issues, this is fine. The cost for comparison against a constant is still extremely cheap. Here's a good read about GPU branching: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter34.html

Regardless, here's a snippet of code that's going to preform much worse than the if statement (and is far less readable/maintainable), but still gets rid of it:

int fx = floor(x);
int y = (fx * y2) + ((1- fx) * y1);
int b = (fx * b2) + ((1 -fx) * b1);

x = 10 * y + b;

Note that I'm making the assumption that x is limited to the range [0, 1]. This won't work if x >= 2 or x < 0.

What that snipped does is convert x to either 0 or 1 and multiply the wrong one by 0 and the other one by 1.

share|improve this answer

Generally it's OK. Shaders will execute in groups of vertices or pixels (different vendors have different terminology for these so I'm keeping away from that) and if all vertices or pixels in a group take the same path then branching cost is negligible.

You also need to trust the shader compiler. The HLSL code you write should not be seen as a direct representation of the bytecode or even assembly that it will compile down to, and the compiler is perfectly free to convert it to something that's equivalent but avoids the branch (e.g. a lerp may sometimes be a preferred conversion). On the other hand, if the compiler determines that performing a branch is actually the faster path, it will compile it down to a branch. Viewing the generated assembly in PIX or a similar tool can be very helpful here.

Finally, the old wisdom still holds here - profile it, determine if it's actually a performance problem for you, and tackle it then, not before. Assuming that something may be a performance problem and acting according to that assumption will only incur a huge risk of bigger problems later on.

share|improve this answer

Quote from the link/article posted by Robert Rouhani:

"Condition codes (predication) are used in older architectures to emulate true branching. If-then statements compiled to these architectures must evaluate both taken and not taken branch instructions on all fragments. The branch condition is evaluated and a condition code is set. The instructions in each part of the branch must check the value of the condition code before writing their results to registers. As a result, only instructions in taken branches write their output. Thus, in these architectures all branches cost as much as both parts of the branch, plus the cost of evaluating the branch condition. Branching should be used sparingly on such architectures. NVIDIA GeForce FX Series GPUs use condition-code branch emulation in their fragment processors."

As mh01 suggested ("Viewing the generated assembly in PIX or a similar tool can be very helpful here."), you should use a compiler tool to examine the output. In my experience, nVidia's Cg tool (Cg is still widely used today because of its cross platform capabilities) gave a perfect illustration of the behavior mentioned in the GPU gems condition codes (predication) paragraph. Thus, regardless of the trigger value, both branches were evaluated on a per fragment basis, and only at the end, the right one was placed in the output registry. Nevertheless, the computation time was wasted. Back then, I thought that branching will help performance, especially because all fragments in that shader relied on an uniform value to decide on the right branch - that did not happen as intended. So, a major caveat here ( e.g. avoid ubershaders - possibly the greatest source of branching hell).

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.