Live data from Hacker News

The Performance Impact of C++'s `final` Keyword

16bpp.net

181–190 of 385 posts

Re: The Performance Impact of C++'s `final` Keyword

#182

What should be evaluated is removing indirection and tightly packing your data. I'm sure you'll gain a better performance improvement. virtual calls and shared_ptr are littered in the codebase. In this way: you can avoid the need for the `final` keyword and do the optimization the keyword enables (de-virtualize calls). >Yes, it is very hacky and I am disgusted by this myself. I would never do this in an actual produc…

In modern C++, macros are a viewed as a code smell because they are strictly worse than alternatives in almost all situations. It is a cultural norm; it is a bit like using "unsafe" in Rust if not strictly required for some trivial case. The C++ language has made a concerted effort to eliminate virtually all use cases for macros since C++11 and replace them with type-safe first-class features in the language. It is a…

But how would one conditionally enable or disable the “final” keyword on class members without a preprocessor macro, even in C++23?

Re: The Performance Impact of C++'s `final` Keyword

#184
post #2

What final enables is devirtualization in certain cases. The main advantage of devirtualization is that it is necessary for inlining. Inlining has other requirements as well -- LTO pretty much covers it. The article doesn't have sufficient data to tell whether the testcase is built in such a way that any of these optimizations can happen or is beneficial.

See this is why I find this odd. Is there a theory as to how devirtualisation could hurt performance?

Practically - it never does. It is always cheaper to perform a direct, possibly inlined, call (devirtualization != inlining) than a virtual one.

Guarded devirtualization is also cheaper than virtual calls, even when it has to do

    if (instance is SpecificType st) { st.Call() }
    else { instance.Call() } 
or even chain multiple checks at once (with either regular ifs or emitting a jump table)

This technique is heavily used in various forms by .NET, JVM and JavaScript JIT implementations (other platforms also do that, but these are the major ones)

The first two devirtualize virtual and interface calls (important in Java because all calls default to virtual, important in C# because people like to abuse interfaces and occasionally inheritance, C# delegates are also devirtualized/inlined now). The JS JIT (like V8) performs "inline caching" which is similar where for known object shapes property access is shape type identifier comparison and direct property read instead of keyed lookup which is way more expensive.

Re: The Performance Impact of C++'s `final` Keyword

#188
I would expect "final" to have no effect on this type of code at all. That it does in some cases cause measurable differences I put down to randomly hitting internal compiler thresholds (perhaps one of the inlining heuristics is "Don't inline a function with more than 100 tokens", and the "final" keyword pushes a couple of functions to 101).

Why would I expect no performance difference? I haven't looked at the code, but I would expect that for each pixel, it iterates through an array/vector/list etc. of objects that implement some common interface, and calls one or more methods (probably something called intersectRay() or similar) on that interface. By design, that interface cannot be made final, and that's what counts. Whether the concrete derived classes are final or not makes no difference.

In order to make this a good test of "final", the pointer type of that container should be constrained to a concrete object type, like Sphere. Of course, this means the scene is limited to spheres.

The only case where final can make a difference, by devirtualising a call that couldn't otherwise be devirtualised, is when you hold a pointer to that type, and the object it points at was allocated "uncertainly", e.g., by the caller. (If the object was allocated in the same basic block where the method call later occurs, the compiler already knows its runtime type and will devirtualise the call anyway, even without "final".)

Post reply on HN