Performance
Profiling tools (perf, VTune, callgrind), CPU optimization techniques, hot-path identification, and benchmarking discipline.
6 questions
SeniorPerformanceVery commonThe code is slow. How do you profile and optimize it?
The code is slow. How do you profile and optimize it?
Measure first with perf or VTune to find the hot path. Identify whether CPU-, memory-, I/O-, or lock-bound. Improve the algorithm, then locality, fewer allocations, devirtualisation, SIMD.
Common mistakes
- ✗Optimising without profiling — the bottleneck is rarely where you think it is; 90% of time is spent in 10% of code
- ✗Micro-optimising non-hot code — optimising a function called once per second while missing the function called 1M times per second
- ✗Benchmarking in debug mode —
-O0disables inlining and SIMD; always benchmark with-O2or-O3
Follow-up questions
- →What is cache-line false sharing and how do you detect and fix it?
- →How does
__builtin_expecthelp the CPU branch predictor?
JuniorTheoryCommonWhen should you optimize C++ code and when should you not?
When should you optimize C++ code and when should you not?
Optimize only after measuring: profile to find the real hot path. Do not optimize before correctness, readability, or evidence of a bottleneck. The maxim 'premature optimization is the root of all evil' applies to the 97% of code that is not hot.
Common mistakes
- ✗Optimizing without a profiler — gut feeling is a notoriously bad guide for where the hot path actually lies
- ✗Skipping algorithmic improvements (O(n²) → O(n log n)) in favour of micro-optimizations on the wrong loop
- ✗Confusing readability cost with runtime cost — unreadable code carries a maintenance bill that often dwarfs the runtime gain
Follow-up questions
- →Which profiler do you reach for first on Linux, and why?
- →What does Amdahl's law tell you about the upper bound of speedup?
MiddlePerformanceCommonWhat is a cache miss and how do you detect/avoid it?
What is a cache miss and how do you detect/avoid it?
A cache miss happens when data isn't in L1/L2/L3 and the CPU fetches from main memory. Detect with perf or cachegrind; avoid via locality, prefetching, and padding to prevent false sharing.
Common mistakes
- ✗Optimising for cache without measuring — cache effects are data-dependent; always profile the actual workload
- ✗Using
alignas(64)on every struct — over-alignment wastes memory and can hurt cache utilisation; apply only to frequently shared atomic data - ✗Confusing false sharing with data sharing — false sharing is two threads writing to different variables that share a cache line; the fix is padding/alignment, not a lock
Follow-up questions
- →What is the difference between L1/L2/L3 cache in terms of latency and typical size on modern CPUs?
- →How does the hardware prefetcher work and when does it fail?
MiddlePerformanceCommonHow does CPU cache hierarchy affect C++ data-structure choice?
How does CPU cache hierarchy affect C++ data-structure choice?
Caches are L1 (~4 cycles), L2 (~12), L3 (~40), DRAM (~200). Contiguous access beats pointer chasing 10–100x. Prefer vector over list and watch for false sharing.
Common mistakes
- ✗Choosing
std::listfor cache locality — pointer chasing is the worst case - ✗Adding mutex per element to avoid false sharing without measuring
- ✗Storing two hot atomics in the same cache line — false sharing destroys throughput
Follow-up questions
- →What is
std::hardware_destructive_interference_size? - →When would you choose SoA over AoS?
SeniorPerformanceCommonWhat are the approaches to code optimization in C++?
What are the approaches to code optimization in C++?
Optimise top-down: better algorithm, then cache-friendly layouts, fewer allocations, compiler flags (-O2/LTO/PGO), devirtualisation, SIMD, parallelism. Profile before tuning.
Common mistakes
- ✗Premature optimisation — spending weeks tuning code that isn't on the critical path; profile first, always
- ✗Trusting the compiler without checking assembly — the compiler might not vectorise a loop you expect it to; use
-fopt-info-vecor godbolt.org to verify - ✗Forgetting that LTO (Link-Time Optimisation) can inline across translation units — enables devirtualisation and constant folding across files; always enable for release builds
Follow-up questions
- →What is PGO (Profile-Guided Optimisation) and what are its build-time costs?
- →How does
std::pmr::monotonic_buffer_resourcecompare to a custom arena allocator?
MiddlePerformanceOccasionalWhat are SIMD instructions? Conditions and usage.
What are SIMD instructions? Conditions and usage.
SIMD applies one instruction to many elements (SSE2 128-bit, AVX 256-bit, AVX-512 512-bit). It needs no data dependencies, aligned access, no per-element branches. Use auto-vectorisation or intrinsics.
Common mistakes
- ✗Using SIMD intrinsics without verifying the target CPU supports the instruction set — link with
-mavx2only if all deployment targets have AVX2; use runtime dispatch otherwise - ✗Unaligned memory loads in SIMD code —
_mm256_loadu_psworks on unaligned but is slower; prefer_mm256_load_pswithalignas(32)data - ✗Mixing SIMD and non-SIMD code in tight loops — context switching between x87/SSE/AVX can cause penalty cycles; keep hot paths homogeneous
Follow-up questions
- →How do you write a function that dispatches at runtime between SSE2 and AVX2 code paths?
- →What is AoS vs SoA layout and how does it affect SIMD efficiency?