4 ms·
Why again are explicit `min` and `max` faster? Is that GLSL specific, and (unlike say C++, where std::min and std::max are just `if (__a < __b) return __a; retu
by lainga 6y ago
Why again are explicit `min` and `max` faster? Is that GLSL specific, and (unlike say C++, where std::min and std::max are just `if (__a < __b) return __a; return __b;`) the compiler won't be able to turn a one-line conditional into an ARB MIN or MAX instruction?
- raphlinus 6y agoThere are two considerations here. One is whether you get the optimal assembly for min and max operations. Using the GLSL intrinsic is probably the best way to get confidence in that, but it's also likely that compilers are smart enough to figure it out when given other input. If it did compile to an actual branch, it would be much slower. Also note on AVX there are VMINPS and VMAXPS instructions. The other consideration is the style of writing the code. If you were writing for a sequential processor with fast branching, it would be very tempting to write "if in the corner, compute this. If exterior on the edge, compute that. etc." This might save quite a bit in the number of "actual work" operations, but is much more likely to compile into branches, which on a GPU (or SIMD or, likely, any modern CPU) the will cost more than the work saved. So it's very idiomatic when writing shader code to use min and max to combine a bunch of cases into a unified code path that can be executed as a straight line.
- anonymoushn 6y agoIt probably compiles to the same code as the equivalent if statements. Source: this person who certainly knows better than I do https://twitter.com/bgolus/status/1235254923819802626 https://twitter.com/bgolus/status/1235254923819802626
- Jasper_ 6y agoThe classic explanation is "divergence", and it goes something like this: On GPUs, branching is tricky because the same code is evaluating many pixels at the same time. If half of those pixels go one way, and half of those pixels go another way, the GPU has to run both pieces of code, with half the results "masked out" [1]. This is why branchless code tends to be more idiomatic in shading languages. You might ask how max() and min() are implemented with a branchless model. Sometimes the GPU has a native instruction for it, and a "sufficiently stupid compiler" might not be able to recognize the branch and turn it into the corresponding max/min. The modern reality is that most all GPUs all have conditional move instructions which allow them to do some amount of branchless conditional across vector lanes like "x >= 1.0 ? x : 0.0;" without incurring the penalty of true flow control. However, some are still uncomfortable with trusting the compiler to recognize and support this, especially on mobile chipsets with poor quality compilers. Others still just prefer the coding style of the idiomatic branchless expressions, since it's what they're used to. [1] Footnote: On super old GPUs, like those in the Direct3D 8 era, flow control was emulated completely through branchless systems. The native machine ISA was something like a series r=lerp(A,B,C)+D instructions, and flow control amounted to clever abuse of this paradigm -- lerping to 0.0 or 1.0 can get you a form of conditional move.