The Performance Impact of C++'s `final` Keyword
171–180 of 385 posts
Re: The Performance Impact of C++'s `final` Keyword
#172Re: The Performance Impact of C++'s `final` Keyword
#173Re: The Performance Impact of C++'s `final` Keyword
#174Re: The Performance Impact of C++'s `final` Keyword
#175I really wish he'd listed all the flags he used. To add on to the flags already listed by some other commenters, `-mcpu` and related flags are really crucial in these microbenchmarks: over such a small change and such a small set of tight loops, you could just be regression on coincidences in the microarchitecture scheduler vs higher level assumptions.
There will be operating system noise that can be in the multi-percent range. This is defined as various OS services that run "in the background" taking up cpu time, emptying cache lines (which may be most important), and flushing a few translate lookaside entries.
Once you recognize the variability from run to run, claiming "1%" becomes less credible. Depending on the noise level, of course.
Linux benchmarks like SPECcpu tend to be run in "single-user mode" meaning almost no background processes are running.
Re: The Performance Impact of C++'s `final` Keyword
#176Earlier quoted context omitted.
Macros in C are a text replace and so it is hard to see from a debugger how th code got like that.
Yes, I'm well aware of the definition of a macro in C and C++. Macros are simpler than templates. You can expand them with a compiler flag.
Re: The Performance Impact of C++'s `final` Keyword
#177Earlier quoted context omitted.
Hard disagree that it's "clearer". I have had to deal with a ton of bugs with people trying to be clever with the `break` logic, or forgetting to put `break` in there at all. if statements are dumber, and maybe arguably uglier, but I feel like they're also more clear, and people don't try and be clever with them.
Updates to languages (don't know where C# is on this) have different types of switch statements that eliminate the `break` problem. For example, with java there's enhanced switch that looks like this var val = switch(foo) { case 1, 2, 3 -> bar; case 4 -> baz; default -> { yield bat(); } } The C style switch break stuff is definitely a language mistake.
Re: The Performance Impact of C++'s `final` Keyword
#178Earlier quoted context omitted.
Jumps/calls are actually be pretty cheap with modern branch predictors. Even indirect calls through vtables, which is the opposite of most programmers intuition. And if the devirtualisation leads to inlining, that results in code bloat which can lower performance though more instruction cache misses, which are not cheap. Inlining is actually pretty evil. It almost always speeds things up for microbenchmarks, as such…
"Inlining is actually pretty evil". No it's not. Except if you __force_inline__ everything, of course. Inlining reduces the number of instructions in a lot of cases. Especially when things are abstracted and factored with lot of indirections into small functions that calls other small functions and so on. Consider a 'isEmpty' function, which dissolves to 1 cpu instruction once inlined, compared with a call/save reg/c…
As you say: “chances are they are a lot better at it than you”. Infrequently they are not.
Re: The Performance Impact of C++'s `final` Keyword
#179Earlier quoted context omitted.
> A large part of becoming a decent engineer [2] for me was learning to stop trusting what professors taught me in college When I was taught about performance, it was all about benchmarking and profiling. I never needed to trust what my professors taught, because they taught me to dig in and find the truth for myself. This was taught alongside the big-O stuff, with several examples where "fast" algorithms are slower…
How do you even get meaningful profiling out of most modern langs? It seems the vast majority of time and calls gets spent inside tiny anonymous functions, GC allocations, and stuff like that.
JVM ecosystem has IntelliJ Idea profiler and similar advanced tools (AFAIK).
.NET has VS/Rider/dotnet-trace profilers (they are very detailed) to produce flamegraphs.
Then there are native profilers which can work with any AOT compiled language that produces canonically symbolicated binaries: Rust, C#/F#(AOT mode), Go, Swift, C++, etc.
For example, you can do `samply record ./some_binary`[0] and then explore multi-threaded flamegraph once completed (I use it to profile C#, it's more convenient than dotTrace for preliminary perf work and is usually more than sufficient).