Fundamentals 16 min read

C++ Performance Traps: Hidden Costs of Abstraction and Compiler Optimization Pitfalls

Modern C++ performance suffers when seemingly harmless abstractions—such as virtual functions, hidden copies, non‑trivial destructors, overused std::shared_ptr, type‑erasing wrappers, std::variant/optional, and std::async—prevent inlining and efficient memory use, while patterns like std::move on returns, hidden destructors in tail recursion, and branchy loops thwart NRVO, tail‑call, and auto‑vectorization optimizations.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
C++ Performance Traps: Hidden Costs of Abstraction and Compiler Optimization Pitfalls

This article discusses common performance pitfalls in C++ programming, categorized into two main areas: "costly abstractions" and "fighting the compiler."

Part 1: Costly Abstractions

The author challenges the misconception of "Zero Cost Abstraction" in C++, citing Chandler Carruth's statement that "C++ does not have zero-cost abstraction." Key pitfalls include:

Virtual Functions: Cause extra pointer indirection, break CPU pipeline prediction, and most importantly, prevent compiler inlining.

Hidden Copies: Occur in member initialization lists (copy twice), for loops (use const reference), lambda captures (use reference or std::move), and implicit type conversions (use auto&).

Hidden Destructors: Complex types in function scopes can cause significant overhead; defining empty destructors makes classes non-trivially-destructible, preventing register-based returns.

std::shared_ptr Overuse: Construction, copying, and destruction involve atomic operations, 10-20% slower than raw pointers; use std::make_shared for better cache locality.

Type Erasure (std::function, std::any): std::function occupies 32 bytes vs 8 for function pointers, involves virtual calls, and may allocate heap memory.

std::variant and std::optional: Have extra memory overhead and don't work well with NRVO.

std::async: Without explicit policy, may execute synchronously; returned futures block on destruction.

Part 2: Fighting the Compiler

This section covers code patterns that prevent compiler optimization:

NRVO (Named Return Value Optimization): Using std::move on return values prevents optimization and causes extra moves.

Tail Recursion: Hidden destructor calls prevent tail-call optimization; use std::string_view instead of std::string.

Auto-vectorization: Avoid if branches and function calls in loops; use templates and inline functions for SIMD optimization.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Performance OptimizationCompiler OptimizationC++NRVOSIMD vectorizationstd::shared_ptrvirtual functionsZero-cost abstraction
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.