#performance
19 posts tagged #performance.
Does custom allocation still pay off in 2026?
Custom allocation used to buy 44 percent. A 2026 re-run of the classic study finds modern allocators like mimalloc have closed most of that gap, so a pmr arena now earns its place for predictable latency and fragmentation resilience rather than raw throughput. With a live allocation-count demo. Episode 3 of the pmr series.
A pmr::vector on a stack buffer never calls new
The HFT and game-loop promise of pmr, made testable: override operator new to count heap allocations, then fill a pmr::vector from a monotonic_buffer_resource backed by a stack buffer with null_memory_resource upstream. Ten thousand pushes, zero heap calls. Episode 2 of the pmr series.
std::pmr is one abstract class with three functions
The whole std::pmr framework is one abstract class, std::pmr::memory_resource, with three functions to override: allocate, deallocate, is_equal. A dozen-line logging resource shows a pmr::vector's allocations, then a monotonic_buffer_resource placed in front of it collapses them to zero. Episode 1 of the pmr series.
The fastest loggers do not format on the calling thread
Every fix in this series synchronizes on the calling thread. The fastest loggers do not: callers enqueue a raw record and a background thread formats and writes it. A mini producer-consumer logger, the spdlog/Quill/NanoLog numbers (around 250 ns versus 7 to 11 ns), and the crash-survivability trade-off. Episode 9 of the concurrent I/O series.
The hottest line is often not the one worth optimizing
A sampling profiler tells you where time is spent, which is not the same as where speeding things up would help. Causal profiling answers the second question directly, and it routinely disagrees with the first. A tour of the profilers worth knowing in 2026: Coz, Tracy, poop, samply, and plain perf.
You can see the dependency chain without running anything
Before reaching for a profiler, read what the compiler already produced. Two loops that add the same floats compile to eight adds into one register versus four independent accumulators, and llvm-mca will predict the throughput difference from that assembly without executing a single instruction. Episode 4 of the verification series.
std::endl is a hidden flush, and clang-tidy flags it
std::endl is not a fancy newline; it is a newline plus a flush, and the flush is what costs you. A flush-counting streambuf proves that a newline flushes zero times and std::endl flushes every time. When to want the flush (crash-survivable logs), when not, and why clang-tidy flags it. Episode 7 of the concurrent I/O series.
Measuring std::regex against a compile-time matcher
std::regex has a reputation, and reputations are worth checking. Measured at its most favourable, with the pattern compiled once outside the timed loop, it takes about seventy times longer per match than CTRE. Include the construction that real code usually pays for and the gap widens by another order of magnitude. Episode 1 of a series on compile-time regular expressions and the language features that make them possible.
{fmt} can do the whole format at compile time
FMT_COMPILE parses the format string during compilation and emits straight-line formatting code, so a format call can be a constant expression with no parsing at runtime. The 12.2 release adds a type-safe C API, a proper C++20 module target, and turns the full Dragonbox cache on by default for faster float formatting.
Converting between std::function and copyable_function nests them
std::function and std::copyable_function are both type-erased callable wrappers and neither recognises the other. Converting between them does not unwrap and rewrap the lambda inside; it wraps the whole previous wrapper. Round-trip in a loop and every call walks a chain of indirections. In this run, 200 round trips made the same calls several hundred times slower.
std::mdspan views one flat buffer as a matrix
std::mdspan is C++23's non-owning multidimensional view. It separates storage (a flat buffer you already have) from shape (extents) from indexing (a layout), so one std::vector becomes a 3x4 matrix with real m[r, c] indexing and no copy. GCC 16.1 ships it, and Mark Hoemmen's C++Now 2026 keynote is on where it and standard parallelism go next.
std::inplace_vector is a vector that never touches the heap
C++26 adds std::inplace_vector<T, N>, a sequence container with a fixed compile-time capacity whose storage lives inside the object. It gives you a vector's dynamic size and push_back with zero heap allocation, which is exactly what embedded, real-time, and hot-path code has been hand-rolling for decades. GCC 16.1 ships it now.
Google Benchmark runs live on Compiler Explorer
You do not need a local build to run a real microbenchmark. Add the benchmark library on Compiler Explorer, turn on execution, and Google Benchmark prints its timing table in the output pane. The demo is also the first lesson every microbenchmark teaches: without benchmark::DoNotOptimize the compiler deletes the loop you are trying to measure and reports a time near zero.
std::flat_map is just two vectors
C++23's std::flat_map is an adaptor over a sorted vector of keys and a parallel vector of values. That buys cache-friendly lookups and near-zero memory overhead, and costs O(n) insertion and aggressive iterator invalidation. It replaces std::map, not std::unordered_map, and knowing which vector trick it is tells you exactly when to reach for it.
Less standard library, faster program
Jussi Pakkanen (creator of Meson) rewrote a subset of the C++ standard library from scratch, dropping ISO conformance to chase compile speed. Converting his real CapyPDF library to it cut compile time ~80% and binary size ~75%, and made the program ~25% faster, with no runtime penalty for the faster build.
One word, final, turns a virtual call into two instructions
A C++ Weekly episode reminded everyone of a free win: marking a class final lets the compiler devirtualize. We took the canonical example to GCC 16.1 at -O2 and read the actual assembly. Without final, GCC hedges with a runtime vtable check; with final, the whole call folds to mov eax, 42; ret. The asm is the proof.
std::rotate: how libstdc++ and libc++ actually differ
Raymond Chen's June 2026 series on The Old New Thing exposed a surprising fact: libstdc++ and libc++ implement std::rotate with completely different algorithms. libstdc++ swaps left-to-right and ends at n-1 swaps with good locality. libc++ decomposes the rotation into gcd(a, n) cycles and hits ~n/2 swaps but with poor locality. Which is faster depends entirely on your input shape.
CUDA 13.3: tile programming in C++ without the boilerplate
NVIDIA CUDA 13.3 (May 26) adds C++ tile programming: declarative tile abstractions replace manual shared memory, synchronization, and indexing. CompileIQ autotuning uses evolutionary algorithms to tune tile sizes and memory layout per kernel (up to 15% speedup on GEMM/attention). Works on Hopper and all other supported architectures.
Could C++ handle an ABI break? The 2026 case
Two pieces dropped in the same week: Luis Caro Campos' CppCon 2025 talk arguing package managers make ABI breaks manageable, and an HFT University article claiming a 58x P99 latency gap between Rust's and C++'s stdlib. The ABI debate is back. Here is what both sides are saying, and what C++26 shipped despite the constraint.