How does an SM5 compilation handle loops and if-else statements, are branching execution routines unwound and if so, to what degree?
Is it only Direct Compute that can optimize branching code?
searchwords: dx11, shadermodel5,
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It only takes a minute to sign up.
Sign up to join this communityHow does an SM5 compilation handle loops and if-else statements, are branching execution routines unwound and if so, to what degree?
Is it only Direct Compute that can optimize branching code?
searchwords: dx11, shadermodel5,
There are two ways if-branching is handled in SM5: predication and dynamic branching. Both of these mechanisms give the same result but have different performance characteristics.
Predication means that the if-statement sets a predication bit on GPU based on the if-condition and every instruction within the non-taken if-branch is effectively turned into a nop-instruction, i.e. GPU checks this bit for each instruction and skips the execution of instructions within the non-taken if-branch at the rate of one instruction/cycle. Because many "normal" instructions (mul, add, etc.) are executed at the full rate of one cycle/instruction anyway this doesn't necessarily result in much of a performance improvement. Some instructions are more expensive though such as texture fetch, transcendental instructions (exp, sin/cos, etc.), double precision math, etc. that are executed at lower-than-full-rate and thus predication can result in some performance improvements, but it depends on GPU architecture what kind of improvements you can get.
Dynamic branching is the mechanism that result in true branching in the execution. There is some overhead for this type of branching though (somewhere around 5 cycles/branch but this depends on the GPU architecture) so it's preferable to use dynamic branching only if you potentially skip larger chunk of code. Also because GPU's execute shader code for several pixels (or more generally "threads") at once in lock-step called warps(NVIDIA)/wavefronts(AMD), branching divergence within a wavefront causes both if-branches be executed for all pixels for the wavefront (with predication). So only if all pixels in a wavefront (around 64 pixels/wave but depends on GPU) take the same branch you can see performance improvements over predication. To enable dynamic branching for given if-statement you need to precede the if-statement with [branch] attribute in HLSL. For more details check: http://msdn.microsoft.com/en-us/library/windows/desktop/bb509610(v=vs.85).aspx
For and while loops can be unrolled or they can use real dynamic looping. There are some attributes in HLSL how this can be controlled as show here (same for both for and while loops): http://msdn.microsoft.com/en-us/library/windows/desktop/bb509602(v=vs.85).aspx. Similarly to if-branching dynamic looping adds some overhead and has branch divergence issues.