Live data from Hacker News

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

16bpp.net

281–290 of 385 posts

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

#281
I do not see how the final keyword would make a difference in performance at all in this case. The compiler should be able to build an inheritance tree and determine by itself which classes are to be treated as final.

Now for libraries, this is a different story. There I can imagine final keyword could have an impact.

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

#282

> And probably, that reason is performance. That's the first problem I see with the article. C++ isn't a fast language, as it is. There are far too many issues with e.g. aliasing rules, lack of proper vectorization (for the runtime arch), etc. If you wish to have a relatively good performance for your code, try ISPC, which still allows you to get great performance with vectorization up to AVX-512, without turning to…

> That's the first problem I see with the article. C++ isn't a fast language, as it is. There are far too many issues with e.g. aliasing rules, lack of proper vectorization (for the runtime arch), etc. That's a bold statement due to the way it heavily contrasts with reality. C++ is ever present in high performance benchmarks as either the highest performing language or second only to C. It's weird seeing someone clai…

> That's a bold statement due to the way it heavily contrasts with reality.

I'm ready to back this up. And no, I'm not confusing things - I work in HPC (realtime computer vision) and in reality the only thing we'd use C++ for is "glue", i.e. binding implementations of the actual algorithms implemented in other languages together.

Implementations could be e.g. in CUDA, ISPC, neural-inference via TensorRT, etc.

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

#283

I do not see how the final keyword would make a difference in performance at all in this case. The compiler should be able to build an inheritance tree and determine by itself which classes are to be treated as final. Now for libraries, this is a different story. There I can imagine final keyword could have an impact.

But dynamically loaded libraries exist, so even if it knows the class is the most derived version out of all classes that exist in all of the statically-linked code through LTO or something, unless it can see the instantiation site it won't be able to devirtualize the function calls without the class being marked as final.

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

#284
post #26

I don't do much C++, but I have definitely found that engineers will just assert that something is "faster" without any evidence to back that up. Quick example, I got in an argument with someone a few years ago that claimed in C# that a `switch` was better than an `if(x==1) elseif(x==2)...` because switch was "faster" and rejected my PR. I mentioned that that doesn't appear to be true, we went back and forth until I…

Yep. "Profiling or it didn't happen." The issue is that it's essentially impossible for even the most neckbeard of us to predict with a high degree of accuracy and precision the performance on modern systems impact of change A vs. change B due to the unpredictable nature of the many variables that are difficult to control including compiler optimization passes, architecture gotchas (caches, branch misses), and interp…

I would say it would be safer to go up to C++17, and there are some goodies there, specially for better compile time stuff.

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

#285
post #20

Earlier quoted context omitted.

"inline" is confusing in C++, as it is not really about inlining. Its purpose is to allow multiple definitions of the same function. It is useful when you have a function defined in a header file, because if included in several source files, it will be present in multiple object files, and without "inline" the linker will complain of multiple definitions. It is also an optimization hint, but AFAIK, modern compiler ig…

> It is useful when you have a function defined in a header file, because if included in several source files, it will be present in multiple object files, and without "inline" the linker will complain of multiple definitions. Traditionally you'd use `static` for that use case, wouldn't you? After all, `inline` can be ignored, `static` can't.

No, because that would make it internal to each object file, while what you want is for all object files to see the same memory location.

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

#286

I do not see how the final keyword would make a difference in performance at all in this case. The compiler should be able to build an inheritance tree and determine by itself which classes are to be treated as final. Now for libraries, this is a different story. There I can imagine final keyword could have an impact.

Only if the complete source code is available to the compiler.

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

#287
post #35
post #6

You should use final to express design intent. In fact I’d rather it were the default in C++, and there was some sort of an opposite (‘derivable’?) keyword instead, but that ship has sailed long time ago. Any measurable negative perf impact should be filed as a bug and fixed.

C++ doesn't have the fragile base problem, as members aren't virtual my default. The only concern with unintended inheritance is with polymorhpic deletion. "final" on class definition disables some tricks thag you can do with private inheritance. Having said that "final" on member functions is great, and I like to see that instead of "override".

All OOP languages have it, the issue is related to changing the behaviour of the base class, and the change introducing unforceen consequences on the inheritance tree.

Changing an existing method way of calling (regular, virtual, static), changing visibility, overloading, introducing a name that clashes downstream, introducing a virtual destructor, making a data member non-copyable,...

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

#288

Earlier quoted context omitted.

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…

Caution! If you compare across languages like that, not all virtual calls are implemented equally. A C++ virtual call is just a load from a fixed offset in the vtbl followed by an indirect call. This is fairly cheap, on modern CPUs pretty much the same as a non-virtual non-inlined call. A Java/C# interface call involves a lot more stuff, because there's no single fixed vtbl offset that's valid for all classes implementing the interface.

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

#289
post #288

Earlier quoted context omitted.

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…

Caution! If you compare across languages like that, not all virtual calls are implemented equally. A C++ virtual call is just a load from a fixed offset in the vtbl followed by an indirect call. This is fairly cheap, on modern CPUs pretty much the same as a non-virtual non-inlined call. A Java/C# interface call involves a lot more stuff, because there's no single fixed vtbl offset that's valid for all classes impleme…

Yes, it is true that there is difference. I'm not sure about JVM implementation details but the reason the comment says "virtual and interface" calls is to outline it. Virtual calls in .NET are sufficiently close[0] to virtual calls in C++. Interface calls, however, are coded differently[1].

Also you are correct - virtual calls are not terribly expensive, but they encroach on ever limited* CPU resources like indirect jump and load predictors and, as noted in parent comments, block inlining, which is highly undesirable.

[0] https://github.com/dotnet/runtime/blob/5111fdc0dc464f01647d6...

[1] https://github.com/dotnet/runtime/blob/main/docs/design/core... (mind you, the text was initially written 18 years ago, wow)

* through great effort of our industry to take back whatever performance wins each generation brings with even more abstractions that fail to improve our productivity

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

#290
post #266

Earlier quoted context omitted.

Not sure about the 10×, either, and if true it would involve more than just the JIT changes. But changing ASP.NET to ASP.NET Core at the same time and the web server as well as other libraries may make it plausible. For certain applications moving from .NET Framework to .NET isn't so simple when they have dependencies and those have changed their API significantly. And in that case most of the newer stuff seems to be…

Roslyn didn't have much of changes in terms of optimizations - it compiles C# to IL so does very little of that, save for switches and certain new or otherwise features like collection literals. You are probably talking about RyuJIT, also called just JIT nowadays :D (the distinction becomes important for targets serviced by Mono, so to outline the difference Mono is usually specified, while CoreCLR and RyuJIT may not…

No, I meant that we've written a compiler, based on Roslyn, whose runtime for compiling the code has improved by 20 % when switching to .NET 6.

And indeed, on the C# -> IL side there's little that's being actually optimized. Besides collection literals there's also switch statements/expressions over strings, along with certain pattern matching constructs that get improved on that side.

Post reply on HN