Is resize + assign faster then reserve + emplace_back for vector?
published at 26.02.2026 18:06 by Jens Weller
Save to Instapaper Pocket
This question I've been asked by Farid Mehrabi on LinkedIn. And after quickly coming up with two tests for this on quick-bench, I've got interesting results with you to share!
One interesting thing I've learned through writing this is that if constexpr is evaluated in all paths if its not in a template context. Which leads to errors if one wants to specialize between different behavior. I've known about std::conditional for a while, but that is one of its use cases. When you want to change a type based on some compile type expression:
auto arr = std::conditional<usestruct,std::vector,std::vector>::type(100);
With usestruct being a constexpr bool variable allowing to switch between the struct and size_t as data types for the vector:
constexpr bool usestruct = false;
I've chosen a setup where I test with a vector of size_t, but also can switch to using a vector of a struct with 3 and later 4 size_t members. Testing with 8 and 24/32 bytes to assign/allocate. Some functionality is in template functions so that if constexpr works as it should.
All types we use in this test are trivial, there is no complicated construction, no allocation by the object created it self. So what we see is what the vector does.
These 3 template functions take care of the basic test functionality. These are template functions in order to make if constexpr work.
template< bool b >
void init_vec(vec& v)
{
for(size_t i = 0; i < v.size(); ++i)
{
if constexpr(b)
{
v[i]={i,i,i,i};
}
else
{
v[i]=i;
}
}
}
This function is only need by the set of test cases that measures the impact when the vector grows. The other set creates an empty vector each time its run.
template< bool b >
void emplace_vec(vec& v)
{
for(size_t i = 0; i < 100;++i)
{
if constexpr(b)
v.emplace_back(i*i,i+i,i,i+1);
else
v.emplace_back(i*i);
}
}
This handles the case of emplace_back.
And handling assign:
template< bool b >
void assign_vec(size_t size,vec& v)
{
for(size_t i = 0; i < 100;++i)
{
//auto l = std::conditional<usestruct,[&](std::vector& arr){arr[i+size]=(mystruct{i*i,i+i,i});},[&](std::vector& arr){arr[i+size]=(i*i);} >::type
//l(arr);
if constexpr(b)
{
v[i+size]={i*i,i+i,i,i+1};
}
else
{
v[i+size]=(i*i);
}
}
}
As I've mentioned I do have two setups to this test. One for measuring the effects if the vector grows with the test. The other creates for every run a new vector, and hence measures more the difference - but its not what most folks do everyday. Lets start with that second setup:
static void reserve_emplace_back(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
auto arr = std::conditional<usestruct,std::vector< mystruct>,std::vector< size_t>>::type(100);
arr.reserve( 100);
emplace_vec(arr);
benchmark::DoNotOptimize(arr);
}
}
// Register the function as a benchmark
BENCHMARK(reserve_emplace_back);
static void resize_assign(benchmark::State& state) {
// Code before the loop is not measured
for (auto _ : state) {
auto arr = std::conditional<usestruct,std::vector< mystruct >,std::vector< size_t>>::type(100);
size_t size = arr.size();
arr.resize(size+100);
assign_vec(size,arr);
benchmark::DoNotOptimize(arr);
}
}
BENCHMARK(resize_assign);
When measuring with size_t the result is assign is faster by 1.1 times:

When measuring with mystruct (the 4xsize_t) reserve emplace is faster by 1.2 times:

Link to the test on quick-bench. So when mostly measuring this, one sees that there is a pivot point where one becomes faster than the other.
This code is to test how this works out with vectors that grow over time:
static void reserve_emplace_back(benchmark::State& state) {
// Code inside this loop is measured repeatedly
auto arr = std::conditional<usestruct,std::vector< mystruct >,std::vector< size_t >::type(100);
init_vec(arr);
for (auto _ : state) {
arr.reserve( 100);
emplace_vec(arr);
if(arr.size() > 10000)
{
auto v = std::conditional<usestruct,std::vector< mystruct >,std::vector< size_t >>::type(100);
std::swap(v,arr);
}
benchmark::DoNotOptimize(arr);
}
}
// Register the function as a benchmark
BENCHMARK(reserve_emplace_back);
static void resize_assign(benchmark::State& state) {
// Code before the loop is not measured
//constexpr bool usestruct = true;
auto arr = std::conditional<usestruct,std::vector< mystruct >,std::vector< size_t >>::type(100);
init_vec(arr);
for (auto _ : state) {
size_t size = arr.size();
arr.resize(size+100);
assign_vec(size,arr);
if(arr.size() > 10000)
{
auto v = std::conditional<usestruct,std::vector< mystruct >,std::vector< size_t >>::type(100);
std::swap(v,arr);
}
benchmark::DoNotOptimize(arr);
}
}
BENCHMARK(resize_assign);
The results now even out more, and there is only an advantage left for vector being 1.1 faster with resize + assign:

With using mystruct, results are equal:

Link to quick-bench. If these results seem relevant to your code base, these should give you a good starting point for your tests to see if and how much you can gain. In generic code you could use if constexpr to switch between both strategies to gain speed. This blog post only compares two approaches, but maybe there is an even better one out there?
Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!