After all, "zero cost abstraction" (aka "optimizer passes") works just as well in C as in C++ ;)
The actual advantage of generics isn't performance, but less (manual) code duplication.
After all, "zero cost abstraction" (aka "optimizer passes") works just as well in C as in C++ ;)
The actual advantage of generics isn't performance, but less (manual) code duplication.
> The actual advantage of generics isn't performance, but less (manual) code duplication.
Or type safety, since code duplication will often be shunned. And the benefit of type safety is there regardless of whether the generics implementation performs specialization.
In postgres' case the whole set of comparison functions isn't know at the compile time of the server - new types / operators can be added at runtime by extensions.
Hence the branches for the few most common/performance critical cases. I'd personally prefer if we gradually moved to C++ in postgres, but that'd not change that we'd need to have those explicit branches to benefit from a faster sort.
Of course it'd be a lot less annoying to implement in C++ - manually implementing templates with macro magic is many things, but not fun.
I'm not sure if specialization is a good argument for generics. Not everyone implements generics through full specialization/monomorphization, and then the performance benefits are not much more likely to emerge than with function pointers or vtables. And many JIT compilers can effectively specialize code involving indirect calls.
The problem is that it more work for the compiler. For example GCC has an early inliner pass that will exploit all easy "inlining" (like those provided by templates) to simplify the code and to expose more opportunities for later passes. On the other hand, indirect function inlining is only done at later stages. The effect is that indirect function call inlining is much less reliable and sometimes not as effective.
It's a bit brittle of course to depend too much on the optimizer, but the whole idea of "zero cost abstraction" in C++ also puts a lot of trust into the optimizer passes.
https://godbolt.org/z/zTe8jocsE
Now tell me I need generics for this to get optimized. (Generics are useful for type safety, which you can add using macros in C, but this is then ugly).
>Now tell me I need generics for this to get optimized.
Generics are explicit and reliably optimized. Implicit function pointer optimization is non-obvious and optimized unreliably.
To reliable devirtualize function pointers in C you need to use non-standard extensions. But the best strategy is not let the optimizer decide and apply this very carefully inly for the few selected cases where it matters.
Choose to believe this, it's your loss. The fact is that performance sensitive domains like browsers/HFT/HPC/ML/etc are almost universally written in heavily templated C++. Just look at linear algebra libraries, C can't compete.
> They create specialized inline code by monomorphization
Neither generics nor monomorphization imply inlining, that's purely a performance optimization.
> (except partially in Go which is a bit smarter).
Are you joking? Go, already an extremely bloated language, somehow managed to implement generics with runtime overhead.
> Generics always create the bloat by default.
Generics only generate code you would have manually written/copied otherwise.
> But the best strategy is not let the optimizer decide and apply this very carefully inly for the few selected cases where it matters.
Two words, heterogeneous programming.
Yes, you just compiled C code as C++. Actually write it as C++ (get rid of the void* mostly) and your program becomes a constant time evaluation: https://godbolt.org/z/5fedo8s5q
I added a little fix which prevents the compiler from discarding the result completely (just by going through __builtin_printf()):
https://godbolt.org/z/W93WrhbaE
PS: another minor tweak (make the comparison function static, so that no extra copy is included in the compiler output):