Back when I was learning to make shaders in Unity, I learned to avoid instructions which would cause branches like the plague. A lot of using 1s and 0s rather than if-statements, etc. Should I still be concerned about that in Godot? If I'm targeting Vulkan, D3D, or OGL, does that change the answer? And if I should concern myself with that, are there any instructions other than if
and switch
that I need to be wary of?
Decamp The short answer is - yes, you should avoid branching. Doesn't matter which language/platform you use because it's a matter of hardware architecture.
The primary reason in shader code is that your GPU runs the same shader code for large number of input pixels/vertices in parallel. If some of those parallel "instances" need to run different branches you'll lose optimal performance.
But this is not all. The machine code instructions on modern hardware are typically run in a multi-stage pipeline. This allows for overlap of consecutive instruction execution, practically running several instructions in parallel. When a branching instruction is pushed down the pipeline, the processing unit has to fetch the next instruction before the branching condition is evaluated. So it will guess which branch to continue pushing into the pipeline. If it predicts wrong, it'll have to flush the pipeline, losing all of the work done on a wrong branch, and start over with the right one. This diminishes the performance boost gained from instruction-level parallelism.
Note that the instruction pipeline thing is also relevant for CPUs. If you're after high performance code, you should minimize branching in general (for example using if
s inside long loops).
As for which high level language instructions are "dangerous", just look at it from perspective of execution paths. If your code can potentially run through multiple execution paths it's a sign that is should be "refactored" so there is only one path. This will, beside if
and switch
also include for
and while
loops with non-constant number of iterations. So you need to watch out for basically all flow control instructions.
I see there's already an answer, but I'll finish mine anyway.
The GPU is very good at running many threads of same code in parallel. Like in a game, running the same shader code for many pixels at the same time. Branches are not executed in parallel, but one at a time.
If you do a simple if statement, the then
branch is first executed, while the threads that take the else
branch are just idling, and then vice versa. A switch with many cases could be catastrophic for performance.
The processing time can be as if every pixel you're drawing takes every single branch.
Note: After seeing the other answer, I'd like to point out that this will not matter with whatever you're doing in gdscript unless it's really bad or has to be really optimized for some insane reason. The only project where CPU branching was a bottleneck for me was pushing an interpreter emulator.
Giza After seeing the other answer, I'd like to point out that this will not matter with whatever you're probably doing in gdscript
Yeah there's a lot of overhead of various kinds in GDScript for CPU branch prediction fails to affect the performance in any significant way. That's why I mentioned it matters only in high performance code, and high level scripting in general cannot be considered high performance.
Stocks for and while loops with non-constant number of iterations.
Does that include if the # of iterations is a uniform?
Decamp Does that include if the # of iterations is a uniform?
No. A uniform is always constant for a run of the shader code. Its value will be the same for all pixels/vertices that are processed in parallel. It cannot cause code to take different execution paths. It's even safe to have if
s with uniforms given that all other values used to calculate the condition are also constants.
uniform int foo;
void fragment(){
const int bar = 3;
if(foo > bar + 1){
doStuffs();
}
}
© 2022 - 2024 — McMap. All rights reserved.