3 ms·
Conclusion: devirtualization optimization is so fragile, so that it's better to avoid using virtual calls in performance-critical code to be sure, that no virtu
by Panzerschrek 5mo ago
Conclusion: devirtualization optimization is so fragile, so that it's better to avoid using virtual calls in performance-critical code to be sure, that no virtual call happens.
- unnah 5mo agoIt's the same problem with autovectorization. Often the only way to tell whether a compiler optimization is successful is to check the produced assembly or benchmark the code. It seems that for devirtualization GCC has a warning option -Wsuggest-final-types which is supposed to tell when devirtualization fails in link-time optimization. Not sure how reliable that is, or whether it will produce gobs of unhelpful warnings. Maybe it could be combined with some kind of hint that we want this particular call to be devirtualized, and don't care about calls without the hint.
- nly 5mo agoThe big problem with virtual calls is it hinders inlining, but if you design your API correctly that can usually be optimised away by design (put loops inside the call, don't call inside a loop)
- Panzerschrek 5mo agoPutting loops inside virtual functions can't work in any cases. Like when a heterogenous collection is iterated and a virtual call is performed for each object. In such cases I prefer using std::variant instead of inheritance, but this works only if all possible types are known ahead of time.
- nly 5mo agoHeterogenous containers are a performance killer for all kinds of other reasons. A vector of variants still wouldn't be optimal. Better to use something like https://www.boost.org/doc/libs/latest/doc/html/poly_collection.html https://www.boost.org/doc/libs/latest/doc/html/poly_collecti...
- 1718627440 5mo agoYeah, no human would work like that. You wouldn't write a sentence for a letter, then go shopping for a single item, then write another sentence, go shopping again, then flush the toilet ...
- gpderetta 5mo ago> A vector of variants still wouldn't be optimal Transpose.
- locknitpicker 5mo ago> Conclusion: devirtualization optimization is so fragile, so that it's better to avoid using virtual calls in performance-critical code to be sure, that no virtual call happens. To be fair, in C++ that has been the ruling rule of thumb for since ever.
- StilesCrisis 5mo agoOnly if you can actually prove that it matters. If you're always going to the same place, the branch predictor can quickly figure that out even if the compiler can't.
- Someone 5mo ago> Only if you can actually prove that it matters. That advice is often given, but I think few C++ programmers follow that to the letter, because you often know beforehand where your performance bottleneck is, and using some simple heuristics upfront may mean you won’t have to spend time benchmarking and refactoring later. > If you're always going to the same place, the branch predictor can quickly figure that out even if the compiler can't. But if the compiler knows which function is called, it can choose to inline it, can’t it? For short functions such as getters and setters, the difference can be huge. “call, move, return” instead of “move” is (ballpark) a factor of three, even ignoring cache pressure. The compiler may even choose not to construct a class instance at all, and keep its state in registers.
- Maxatar 5mo agoBranch prediction is not magic and code that relies on it for branches that could otherwise be deduced statically can quickly result in destructive aliasing which results in branch mispredictions even on highly predictable branches.