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

Role and Effects of final

  • final primarily 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 final may 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-vtables or “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/cold to 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 final primarily 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 like pure/const arise; consensus is that compilers are good at many optimizations already, but explicit qualifiers still help both optimization and correctness.