The Performance Impact of C++'s `final` Keyword
Role and Effects of final
finalprimarily enables devirtualization, which can then allow inlining of virtual calls in some situations.- It serves as a compile‑time assertion: “this class/method will not be overridden,” helping compilers and humans reason about code.
- Several commenters stress that in many real code paths, especially calls via base pointers or interfaces, marking concrete classes
finalmay not actually enable devirtualization.
LTO, Whole‑Program Assumptions, and Devirtualization
- With dynamic linking and plugins, the compiler/linker cannot assume no new subclasses will appear at runtime.
- Flags like Clang’s
-fwhole-program-vtablesor “this is the whole program” options can unlock more aggressive devirtualization, but are considered risky to enable by default. - GCC and Clang differ: GCC does more devirtualization under LTO; Clang often doesn’t unless given stronger whole‑program guarantees. There are suggestions some behaviors might be bugs or missed optimizations.
Inlining: Benefits and Risks
- Devirtualization doesn’t necessarily imply inlining, but inlining is often the main performance win when virtual dispatch is removed.
- Benefits: avoids call overhead, enables constant propagation, better optimization across function boundaries.
- Risks: code bloat and instruction‑cache misses. Multiple commenters note over‑aggressive inlining can slow large programs even though it speeds up microbenchmarks.
- Some practitioners frequently use
noinline/coldto prevent inlining where it hurts icache or pollutes hot paths.
Benchmarks and Methodology Concerns
- Many argue the reported 1%‑level differences are likely within noise: OS jitter, code layout, instruction alignment, cache warmup, and compiler heuristics.
- Critiques: missing full compiler flags, insufficient repetition, no assembly/IR comparison, no perf counters, no binary size analysis.
- Some suspect Clang regressions in the article might be compiler bugs or bad inlining decisions rather than inherent to
final.
When to Use final
- Strong sentiment: use
finalprimarily to express design intent and prevent unintended inheritance; treat performance impact as an optimization bonus. - Several would prefer “final by default, opt‑in to inheritance,” citing safety and clarity; others worry about needing escape hatches when extending third‑party code.
Related Language Features
- Comparisons with
const,constexpr,inline, and attributes likepure/constarise; consensus is that compilers are good at many optimizations already, but explicit qualifiers still help both optimization and correctness.