C++'s `noexcept` can sometimes help or hurt performance

Deterministic randomness & standard-library portability

  • Several comments focus on std::uniform_int_distribution being implementation-defined across libraries, breaking deterministic tests and fuzzing.
  • Some see this as a major flaw: a “standard” library that behaves differently per platform is called “absurd” and “under/over-specified.”
  • Others defend the choice: different architectures (CPUs, GPUs) need different algorithms; over-specifying implementations (as with std::regex and standard hash tables) is blamed for bad performance.
  • Suggested workaround: use Boost or other libraries when reproducible randomness is required.

Spec vs single reference implementation

  • One camp argues the C++ committee should ship code (a single stdlib implementation) instead of a spec, citing Rust’s approach and faster evolution.
  • The opposing camp sees a spec as a long-term strength: cross-vendor experimentation, platform-specific optimizations, and lessons feeding back into the standard.
  • Disagreement on whether multiple “real” standard library implementations meaningfully exist; many cited alternatives (EASTL, HPX, Abseil, Folly, libcu++) are partial or non-std::-namespace.

What noexcept actually does

  • Clarified that noexcept does not statically forbid throwing; it instead guarantees that if an exception escapes, std::terminate is called.
  • That guarantee must be implemented, adding metadata and complexity; some compilers historically inhibited inlining across noexcept boundaries.

Performance: where noexcept helps

  • Clear wins:
    • std::vector uses move instead of copy only if moves are noexcept, affecting reallocation cost.
    • std::unordered_* may avoid storing hashes if the hash functor is noexcept, reducing memory.
  • On “zero-cost” EH platforms, normal execution pays almost no runtime cost; noexcept mainly affects code size and certain optimization choices.

Performance: where noexcept can hurt or confuse

  • Overusing noexcept (marking functions that can throw) forces compilers to maintain termination paths and unwinding metadata, potentially bloating binaries or affecting inlining.
  • Some backend engineers argue noexcept was oversold as a free optimization; inlining with mixed noexcept/non-noexcept regions is especially tricky.
  • One proposed alternative (not adopted): make violations UB so compilers can treat noexcept as a pure optimization hint.

Compiler analysis & checked-exception analogies

  • Compilers can infer that some functions don’t throw, but cannot in general decide this for all call graphs.
  • Some wished for Java-like checked exceptions or a stricter noexcept that only calls other noexcept code, but others note practical issues with C libraries and existing ecosystems.

Exceptions, tooling, and methodology

  • Some projects simply compile with -fno-exceptions to avoid overhead and complexity.
  • Notes on implementation:
    • Historical GCC unwinding used a global lock; newer work aims to scale better.
    • On some platforms, overhead is mostly in unwind tables, not instruction cost.
  • Several commenters criticize the article’s benchmarks:
    • Example code sometimes shows identical assembly for noexcept vs non-noexcept.
    • Timing methodology (choice of clock, lack of clear harness) is questioned.
  • General advice: use tools like Compiler Explorer to inspect codegen rather than relying on folklore about exceptions and noexcept.