This is of course an optimization which is hyper specific and in fact near useless in practice.
Converting an awful algorithm into a superior form (from O(N) to O(1)) is a neat trick, but obviously can only apply if:
- Such an algorithm can be easily detected by the Compiler and there is actually a replacement algorithm which does exactly the same thing within the defined behavior. These cases are very rare.
- The programmer is unable or unwilling to identify the optimization, which in most cases means he does not understand his algorithm well or does not care about performance.
Just as an example this substitutions CAN NOT be applied if you replace ints with floats. Of course the O(1) algorithm is still generally preferable in that case a Compiler will never help you with that if you use floats.
Actually that was the reason Compiler Explorer was created in first place, Matt Godbolt wanted an easy way to sort out all the usual discussions "I know better than the compiler" in regards to using C++ abstractions.
Does someone know how the compiler comes up with the closed form solution? Is there a hardcoded list of common patterns and their solution in the compiler, or is this really generated from the code itself?
Yeah, but all micro optimisations come with costs, from compiler time to maintenance overhead. Someone's still got to ask if it's worth it. And in this case, I just can't see many regular programs containing code likely to trip this, but benchmarks? All the time.
Sure, but in the scenario you describe, (count-1)*(count-2) also overflows, and that's why the generated code already to 64 bits for the multiply. (See my cousin comment to this one.)
I feel like you're probably right, but I can't figure out the exact reason.
The multiplication of (n-1)(n-2) already expands to 64 bits. It has to: Even when the loop accumulator doesn't overflow when counting up to n(n-1)/2 in the abstract machine, (n-1)(n-2) will be larger (and possibly overflow 32 bits) for most n. (The highest valid n is 92682, I think.)
So surely once you're doing that, I'd think n(n-1)/2 can be done in 64 bits just as easily.
Converting an awful algorithm into a superior form (from O(N) to O(1)) is a neat trick, but obviously can only apply if: - Such an algorithm can be easily detected by the Compiler and there is actually a replacement algorithm which does exactly the same thing within the defined behavior. These cases are very rare. - The programmer is unable or unwilling to identify the optimization, which in most cases means he does not understand his algorithm well or does not care about performance.
Just as an example this substitutions CAN NOT be applied if you replace ints with floats. Of course the O(1) algorithm is still generally preferable in that case a Compiler will never help you with that if you use floats.