.. /index

push_back_unchecked

push_back_unchecked

push_back_unchecked is a non-standard vector operation that skips the size == capacity check, transferring responsibility for ensuring space to the caller. The performance impact is dramatically larger than the obvious "save one branch" intuition suggests: up to 5.9× speedup at small sizes on Clang because removing the branch lets the compiler fully vectorize the loop. At 10M elements the speedup drops to 10–30%, but the technique remains a clean win in any context where the caller knows the upper bound.

Why removing one branch is worth 5.9×

Inside a tight push_back loop, the compiler is constantly checking if (size_ == capacity_). That branch:

When push_back_unchecked removes the check, the loop becomes a simple memory write per iteration. Clang autovectorizes it to AVX2/AVX-512 stores. The compiler can hoist the size pointer increment out of the loop body and apply it once at the end. The 5.9× number is the realistic impact of this transformation on small-to-medium sizes (where the branch dominates) — in 1000-int benchmarks: 540 ns → 93 ns.

The MSVC penalty

Research from The Coding Nest measured MSVC's std::vector running 30–80% slower than a hand-rolled reimplementation even in release builds. The penalty seems to come from a combination of debug-iterator support that survives release optimization, conservative inlining, and exception-safety bookkeeping that other implementations elide. A push_back_unchecked shim around MSVC's vector recovers most of the gap.

How to use it safely

The standard pattern:

v.reserve(known_upper_bound);
for (...) {
    v.push_back_unchecked(value);  // safe: we reserved enough
}

The caller's reserve makes the unchecked path correct. Bugs creep in when the upper bound is computed incorrectly — at which point you get a buffer overflow, not a graceful resize. This is one of those "safe by construction in the right context, unsafe by construction in the wrong context" patterns; it belongs in tightly-scoped hot loops, not as a default operation.

Standard library availability

std::vector does not provide push_back_unchecked. folly-fbvector and absl-inlinedvector do, sometimes spelled unchecked_emplace_back or similar. Rust's Vec exposes set_len plus raw pointer writes (as_mut_ptr().add(i)), which together build the equivalent. Boost's containers expose it as unchecked_push_back.

Where it fits

push_back_unchecked is the smallest of the five major dynamic-array optimizations in absolute terms — realloc/mremap and good allocators dominate it. But it is the easiest to apply in a hot loop where you already know the size. For tight inner loops over bounded inputs, it is essentially free performance.

See fastest-dynamic-arrays for the broader hierarchy of optimizations.

Linked from

Sources