The commonly received wisdom about std::vector is that it doubles in size each time it needs to grow beyond its capacity. Though other commonly used grow factors include 1.5.
The amortised time complexity of O(1) for insert implies a exponential increase as allocating a fixed amount would have a complexity of O(N). See for example:
How does the capacity of std::vector grow automatically? What is the rate?
This is a simplication of course. We can also expect rounding to multiples of the page size at large enough values.
Increasing size by a factor is fine for the general case but what happens in more extreme cases?
Say I reserve more than half the available free ram or I successfully reserve a really huge buffer (>100Gb?) based on an estimate rather than a known size.
If I then need to add one more element than I expected what happens?
Is any implementation of vector clever enough to not try to double the memory in these cases? Is there some limit to the size increase after which doubling stops and it becomes linear or some other strategy is used?
The exact growth algorithm is of course implementation defined.
So I will state that I'm interested in the version of libstdc++ that ships with g++ (any version) on GNU/Linux.
I have not so far been able to decipher the code myself.
Information about other implementations would be interesting too. As would information about non-standard APIs that allow the behaviour to be modified.
See also:
- A different multiplier for the memory management of std::vector
- What is the ideal growth rate for a dynamically allocated array?
Note I realize that taking the currently available memory into account would be challenging to implement. Realistically there is no safe way to determine the largest free contiguous memory block available See for example https://stackoverflow.com/a/690494/1569204
On the other hand it would not be unreasonable to allow exponential growth up to say 10Gb on a system with 50Gb total RAM but up to say 100Gb on a system with 500Gb available.
It is also noted that relying on implementation defined behaviour would generally be bad form. It would be safer to wrap std::vector and control its allocation behaviour yourself if that is really necessary. For example
class foo
{
std::vector<int> bar;
public:
void reserveExtra(const size_t atLeastNMoreElements)
{
if ((bar.capacity() - bar.size()) < atLeastNMoreElements)
{
bar.reserve(bar.size()+atLeastNMoreElements);
}
}
void wrapPush(int x)
{
if (bar.size() == bar.capacity()) // slight overhead
{
// your re-allocation strategy here
}
else
{
bar.push_bach(x); //use default behaviour
}
}
};
and of course consider using deque instead. See for example - http://www.gotw.ca/gotw/054.htm