std::vector capacity smart implementation - c++

I know that std::vector capacity behavior is implementation specific, is there any smart implementation that does this :
vector<int> v;
for(int i = 0; i < 10000 ; ++i){
v.push_back(i);
}
At initialisation, it can predict the capacity of the 'vector', in this example it will initiate the capacity to 10000
I am asking for this because I always thought gcc does this kind of predictions, but I couldn't find anything about this ... I think I have seen this somewhere, so is there any implementation that does this ?

Nothing get predicted. However:
one can use reserve to preallocate the maximum required amount of elements. push_back will then never need to reallocate.
push_back use the growth strategy of vector that allocate more than just one mor element. IIRC the growth factor is 2, which means that the number of reallocation in a serie of push_back tends to become logarithmic. Therefore, the cost of N calls to push_back converges toward log2(N).

It exists different constructor for std::vector. One of these possibilities is to say the default value and the number of values that you want to your vector.
From the documentation of std::vector:
// constructors used in the same order as described above:
std::vector<int> first; // empty vector of ints
std::vector<int> second (4,100); // four ints with value 100
std::vector<int> third (second.begin(),second.end()); // iterating through second
std::vector<int> fourth (third); // a copy of third
This is useful if you know in advance the maximum size of your vector.

Related

Performance impact when resizing vector within capacity

I have the following synthesized example of my code:
#include <vector>
#include <array>
#include <cstdlib>
#define CAPACITY 10000
int main() {
std::vector<std::vector<int>> a;
std::vector<std::array<int, 2>> b;
a.resize(CAPACITY, std::vector<int> {0, 0})
b.resize(CAPACITY, std::array<int, 2> {0, 0})
for (;;) {
size_t new_rand_size = (std::rand() % CAPACITY);
a.resize(new_rand_size);
b.resize(new_rand_size);
for (size_t i = 0; i < new_rand_size; ++i) {
a[i][0] = std::rand();
a[i][1] = std::rand();
b[i][0] = std::rand();
b[i][1] = std::rand();
}
process(a); // respectively process(b)
}
}
so obviously, the array version is better, because it requires less allocation, as the array is fixed in size and continuous in memory (correct?). It just gets reinitialized when up-resizing again within capacity.
Since I'm going to overwrite anyway, I was wondering if there's a way to skip initialization (e.g. by overwriting the allocator or similar) to optimize the code even further.
so obviously,
The word "obviously" is typically used to mean "I really, really want the following to be true, so I'm going to skip the part where I determine if it is true." ;) (Admittedly, you did better than most since you did bring up some reasons for your conclusion.)
the array version is better, because it requires less allocation, as the array is fixed in size and continuous in memory (correct?).
The truth of this depends on the implementation, but the there is some validity here. I would go with a less micro-managementy approach and say that the array version is preferable because the final size is fixed. Using a tool designed for your specialized situation (fixed size array) tends to incur less overhead than using a tool for a more general situation. Not always less, though.
Another factor to consider is the cost of default-initializing the elements. When a std::array is constructed, all of its elements are constructed as well. With a std::vector, you can defer constructing elements until you have the parameters for construction. For objects that are expensive to default-construct, you might be able to measure a performance gain using a vector instead of an array. (If you cannot measure a difference, don't worry about it.)
When you do a comparison, make sure the vector is given a fair chance by using it well. Since the size is known in advance, reserve the required space right away. Also, use emplace_back to avoid a needless copy.
Final note: "contiguous" is a bit more accurate/descriptive than "continuous".
It just gets reinitialized when up-resizing again within capacity.
This is a factor that affects both approaches. In fact, this causes your code to exhibit undefined behavior. For example, let's suppose that your first iteration resizes the outer vector to 1, while the second resizes it to 5. Compare what your code does to the following:
std::vector<std::vector<int>> a;
a.resize(CAPACITY, std::vector<int> {0, 0});
a.resize(1);
a.resize(5);
std::cout << "Size " << a[1].size() <<".\n";
The output indicates that the size is zero at this point, yet your code would assign a value to a[1][0]. If you want each element of a to default to a vector of 2 elements, you need to specify that default each time you resize a, not just initially.
Since I'm going to overwrite anyway, I was wondering if there's a way to skip initialization (e.g. by overwriting the allocator or similar) to optimize the code even further.
Yes, you can skip the initialization. In fact, it is advisable to do so. Use the tool designed for the task at hand. Your initialization serves to increase the capacity of your vectors. So use the method whose sole purpose is to increase the capacity of a vector: vector::reserve.
Another option – depending on the exact situation — might be to not resize at all. Start with an array of arrays, and track the last usable element in the outer array. This is sort of a step backwards in that you now have a separate variable for tracking the size, but if your real code has enough iterations, the savings from not calling destructors when the size decreases might make this approach worth it. (For cleaner code, write a class that wraps the array of arrays and that tracks the usable size.)
Since I'm going to overwrite anyway, I was wondering if there's a way to skip initialization
Yes: Don't resize. Instead, reserve the capacity and push (or emplace) the new elements.

Can std::vector capacity/size/reserve be used to manually manage vector memory allocation?

I'm running very time sensitive code and would need a scheme to reserve more space for my vectors at a specific place in the code, where I can know (approximately) how many elements will be added, instead of having std do it for me when the vector is full.
I haven't found a way to test this to make sure there are no corner cases of std that I do not know of, therefore I'm wondering how the capacity of a vector affects the reallocation of memory. More specifically, would the code below make sure that automatic reallocation never occurs?
code
std::vector<unsigned int> data;
while (condition) {
// Reallocate here
// get_elements_required() gives an estimate that is guaranteed to be >= the actual nmber of elements.
unsigned int estimated_elements_required = get_elements_required(...);
if ((data.capacity() - data.size()) <= estimated_elements_required) {
data.reserve(min(data.capacity() * 2, data.max_length - 1));
}
...
// NEVER reallocate here, I would rather see the program crash actually...
for (unsigned int i = 0; i < get_elements_to_add(data); ++i) {
data.push_back(elements[i]);
}
}
estimated_elements_required in the code above is an estimate that is guaranteed to be equal to, or greater than, the actual number of elements that will be added. The code actually adding elements performs operations based on the capacity of the vector itself, changing the capacity halfway through will generate incorrect results.
Yes, this will work.
From the definition of reserve:
It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity().

Filling a vector with out-of-order data in C++

I'd like to fill a vector with a (known at runtime) quantity of data, but the elements arrive in (index, value) pairs rather than in the original order. These indices are guaranteed to be unique (each index from 0 to n-1 appears exactly once) so I'd like to store them as follows:
vector<Foo> myVector;
myVector.reserve(n); //total size of data is known
myVector[i_0] = v_0; //data v_0 goes at index i_0 (not necessarily 0)
...
myVector[i_n_minus_1] = v_n_minus_1;
This seems to work fine for the most part; at the end of the code, all n elements are in their proper places in the vector. However, some of the vector functions don't quite work as intended:
...
cout << myVector.size(); //prints 0, not n!
It's important to me that functions like size() still work--I may want to check for example, if all the elements were actually inserted successfully by checking if size() == n. Am I initializing the vector wrong, and if so, how should I approach this otherwise?
myVector.reserve(n) just tells the vector to allocate enough storage for n elements, so that when you push_back new elements into the vector, the vector won't have to continually reallocate more storage -- it may have to do this more than once, because it doesn't know in advance how many elements you will insert. In other words you're helping out the vector implementation by telling it something it wouldn't otherwise know, and allowing it to be more efficient.
But reserve doesn't actually make the vector be n long. The vector is empty, and in fact statements like myVector[0] = something are illegal, because the vector is of size 0: on my implementation I get an assertion failure, "vector subscript out of range". This is on Visual C++ 2012, but I think that gcc is similar.
To create a vector of the required length simply do
vector<Foo> myVector(n);
and forget about the reserve.
(As noted in the comment you an also call resize to set the vector size, but in your case it's simpler to pass the size as the constructor parameter.)
You need to call myVector.resize(n) to set (change) the size of the vector. calling reserve doesn't actually resize the vector, it just makes it so you can later resize without reallocating memory. Writing past the end of the vector (as you are doing here -- the vector size is still 0 when you write to it) is undefined behavior.

Multidimensional vector bus error

I have a 11663 Bus Error when I attempt to do the following;
std::vector< std::vector<int> > bullets;
std::vector<int> num;
num[0] = 7;
bullets.push_back(num);
I thought this would work as the vector bullets's type is a vector. Why doesn't this work as expected? Also, the following works;
std::vector< std::vector<int> > bullets;
std::vector<int> num (4, 100);
bullets.push_back(num);
And I don't know why this works, but not my other code.
std::vector<int> num;
num[0] = 7;
num has not yet allocated storage for anything. Only use the indexing syntax [] if you know an element exists at that index. Otherwise, use push_back, which grows the vectors storage capacity if needed. The second example works because you used the constructor which reserves a certain amount of space for elements (4 in this case, all with the value 100).
std::vector<int> num;
num.push_back(7);
bullets.push_back(num);
On a side note, "this doesn't work" is not a very helpful problem description. Also, note that a vector of vectors used as a matrix is not a good idea in performance critical code should you need to iterate over each element.
Don't scrap it just yet and don't worry abut it unless you know for a fact that it will be a problem, but realize that you lose locality of data with this approach because each vector will allocate its storage separately. If this data is being iterated over in a tight loop you are better off allocating one big vector and calculating the offset to each individual position manually.
num[0] = 7;
should be
num.push_back(7);

Efficiency when populating a vector

Which would be more efficient, and why?
vector<int> numbers;
for (int i = 0; i < 10; ++i)
numbers.push_back(1);
or
vector<int> numbers(10,0);
for (int i = 0; i < 10; ++i)
numbers[i] = 1;
Thanks
The fastest would be:
vector <int> numbers(10, 1);
As for your two methods, usually the second one; although the first one avoids the first zeroing of the vector in the constructor, it allocates enough memory from the beginning, avoiding the reallocation.
In the benchmark I did some time ago the second method won even if you called reserve before the loop, because the overhead of push_back (which has to check for each insert if the capacity is enough for another item, and reallocate if necessary) still was predominant on the zeroing-overhead of the second method.
Note that this holds for primitive types. If you start to have objects with complicated copy constructors generally the best performing solution is reserve + push_back, since you avoid all the useless calls to the default constructor, which are usually heavier than the cost of the push_back.
In general the second one is faster because the first might involve one or more reallocations of the underlying array that stores the data. This can be aleviated with the reserve function like so:
vector<int> numbers;
numbers.reserve(10);
for (int i = 0; i < 10; ++i)
numbers.push_back(1);
This would be almost close in performance to your 2nd example since reserve tells the vector to allocate enough space for all the elements you are going to add so no reallocations occur in the for loop. However push_back still has to check whether vector's size exceeds it's current capacity and increment the value indicating the size of the vector so this will still be slightly slower than your 2nd example.
In general, probably the second, since push_back() may cause reallocations and resizing as you proceed through the loop, while in the second instance, you are pre-sizing your vector.
Use the second, and if you have iota available (C++11 has it) use that instead of the for loop.
std::vector<int> numbers(10);
std::iota(numbers.begin(), numbers.end(), 0);
The second one is faster because of preallocation of memory. In the first variant of code you could also use numbers.reserve(10); which will allocate some memory for you at once, and not at every iteration (maybe some implementation does more bulky reservation, but don't rely on this).
Also you'd better use iterators, not straight-forward access. Because iterator operation is more predictable and can be easely optimized.
#include <algorithm>
#include <vector>
using namespace std;
staitc const size_t N_ELEMS = 10;
void some_func() {
vector<int> numbers(N_ELEMS);
// Verbose variant
vector<int>::iterator it = numbers.begin();
while(it != numbers.end())
*it++ = 1;
// Or more tight (using C++11 lambdas)
// assuming vector size is adjusted
generate(numbers.begin(), numbers.end(), []{ return 1; });
}
//
There is a middle case, where you use reserve() then call push_back() a lot of times. This is always going to be at least as efficient than just calling push_back() if you know how many elements to insert.
The advantage of calling reserve() rather than resize() is that it does not need to initialise the members until you are about to write to them. Where you have a vector of objects of a class that need construction, this can be more expensive, especially if the default constructor for each element is non-trivial, but even then it is expensive.
The overhead of calling push_back though is that each time you call it, it needs to check the current size against the capacity to see if it needs to re-allocate.
So it's a case of N initializations vs N comparisons. When the type is int, there may well be an optimization with the initializations (memset or whatever) allowing this to be faster, but with objects I would say the comparisons (reserve and push_back) will almost certainly be quicker.