Live data from Hacker News

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

16bpp.net

171–180 of 385 posts

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

#175

I 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.

And he didn't repeat each test case 5 or 9 times, and take the median (or even an average).

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

#176
post #33

Earlier 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.

when things get complex templete error messages are easier to follow. nobody makes complex macros but if you tried. (template error messeges are legendary for a reason. nested macros are worse)

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

#177
post #57

Earlier 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.

This is just forcing return value. You either have to break or return at the branches. To me they all look equivalent

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

#178
post #117

Earlier 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…

The “evilness” is just that sometimes if you inline aggressively in a microbenchmark things get faster but in real programs things get slower.

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

#179
post #127
post #125

Earlier 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.

This is easy in most modern programming languages.

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).

[0] https://github.com/mstange/samply

Post reply on HN