I can think of three ways of filling a std::vector
Suppose we have
vector<int> v(100, 0);
Then I want it to hold (1, 1, 1). We can do:
v.clear();
v.resize(3, 1);
Or
v = vector<int>(3, 1);
And I learned another approach:
vector<int>(3, 1).swap(v);
First question is: Is any of them the best approach?
Second question: suppose v was declared outside the main function. According to this answer, the memory will be allocated in the data segment. If I use the second or third approach, will the memory be allocated on the stack?
How about you use the member of vector that is there for this task?
std::vector<int> v(100);
v.assign(3, 1); // this is what you should do.
So, here's the differences and I'll let you decide what is best for your situation.
v.clear();
v.resize(3, 1);
In this case we have marked the vector as cleared. It still holds whatever it allocated in order to hold 100 elements (which can be more than the space necessary for 100 elements). Then we added 3 items with value of 1. All this did was increase the size counter and reset 3 values, the underlying memory is still the same size.
v = vector<int>(3, 1);
This does pretty much the same thing except that an extra vector temporary is created and instead of there being intermittent places where the counter is 0 and then 3 with some values, it simple copies the counter size and then does an operation similar to a memcpy to copy over the 3 elements. Size of underlying memory allocated for v is still enough to hold 100 integers.
vector<int>(3, 1).swap(v);
This one is significantly different. In this case we create a temporary vector that holds 3 elements that are all initialized to 1. Theoretically it could still have enough memory reserved for 100 elements but the chances are it has much less. Then we swap this vector with our own and let the temporary get destroyed. This has the added benefit of clearing out any extra memory allocated by our old vector that wasn't in the temporary. The way this works is that the two vectors (our v and the temporary) swap more than just counters and values, they also swap buffer pointers.
This is the only way to shrink a vector.
To answer the second question first: vector will always dynamically allocate the memory for the objects it contains, so it will end up on the heap.
As to which method of reassigning is better, I'd say your first or second method make your intent most clear, and that's the most important attribute.
Swapping will effectively shrink the vector to 3 elements. The other ones will likely not.
vector<int> v(100);
v.assign(3, 1);
assert(v.size() == 3);
assert(v.capacity() != 3);
v = vector<int>(3, 1);
// Now, v.capacity() is likely not to be 3.
vector<int>(3, 1).swap(v);
assert(v.capacity() == 3);
The other approaches won't resize the vector internally. It still will occupy 100 * sizeof(int) bytes in memory, even if the size() member returns 3. Try displaying v.capacity() to convince yourself.
One issue, not mentioned in previous posts, is important in choosing among these alternatives. Namely, exception safety. The vector<int>(3, 1).swap(v); has strong exception safety guarantee. The form v = vector<int>(3, 1); might also offer such guarantee if assignment implemented in terms of swap. The first alternative is not safe: v.clear(); v.resize(3, 1);
Related
What is the expected difference (if any) in memory taken by vvint1 and vvint2?
Is vitest1 copied into a new memory position each time a push_back takes place?
Is vitest2 copied into a new memory position each time a push_back takes place?
typedef vector<int> vint_t;
typedef vector<vint_t> vvint_t;
size_t nvec = 2;
size_t nvvec = 3;
vvint_t vvint1(nvvec), vvint2(nvvec);
vint_t vitest2(nvec, 1);
for ( j = 0; j < nvvec; j++ ) {
vint_t vitest1(nvec, 2);
vvint1.push_back(vitest1);
vvint2.push_back(vitest2);
}
Both vvint1 and vvint2 are initially created with nvvec = 3 default-constructed members (i.e. empty vectors of int).
push_back always either copies or moves, but in this case you're not supplying rvalue references so you'll get copies. Look into std::move for more on that.
You're pushing the same number of things to both vectors. Therefore both vvint1 and vvint2 will end up being the same size.
vvint1 and vvint2 memory requirements are:
(on stack, in the example) sizeof(vector<vector<int>>) for the objects themselves, which is the same (vector is 2–3 pointers usually, regardless of the inner type);
(on heap) 2 * nvvec * sizeof(vector<int>) for the contents (nvvec initially and nvvec push_back-ed in the loop); again, that’s the same for vvint1 and vvint2;
(on heap) contents of each vector stored in these vectors. Since vectors don’t share memory, and you store them by value, nvec * nnvec * sizeof(int). Again, the same.
So the overall requirements are the same:
sizeof(vector<vector<int>>) + nvvec * sizeof(vector<int>) + nvec * nnvec * sizeof(int)
Plain vector<int> would take less space ofc as item 2 wouldn’t apply. But what is more important is that in vvint_t, inner vectors may be of different lengths, and resizing any inner vector doesn’t affect others. But that adds complexity, so unless you really need that, it’s simpler to use flat vector and calculate index; imaging libraries do it that way.
Regarding second part, both vitests are copied on each push_back. But since C++11, you can write vvint1.push_back(std::move(vitest1)); (or vvint1.emplace_back(std::move(vitest1));) to move instead. For vectors that means the newly-constructed vector takes ownership of vitest1 contents without copying it (so vitest1 becomes empty). That doesn’t change memory requirements but reduces allocations as the space allocated by vitest (at construction) would be reused instead of being freed (at destruction, at end of each iteration).
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.
I have data which is N by 4 which I push back data as follows.
vector<vector<int>> a;
for(some loop){
...
a.push_back(vector<int>(4){val1,val2,val3,val4});
}
N would be less than 13000. In order to prevent unnecessary reallocation, I would like to reserve 13000 by 4 spaces in advance.
After reading multiple related posts on this topic (eg How to reserve a multi-dimensional Vector?), I know the following will do the work. But I would like to do it with reserve() or any similar function if there are any, to be able to use push_back().
vector<vector<int>> a(13000,vector<int>(4);
or
vector<vector<int>> a;
a.resize(13000,vector<int>(4));
How can I just reserve memory without increasing the vector size?
If your data is guaranteed to be N x 4, you do not want to use a std::vector<std::vector<int>>, but rather something like std::vector<std::array<int, 4>>.
Why?
It's the more semantically-accurate type - std::array is designed for fixed-width contiguous sequences of data. (It also opens up the potential for more performance optimizations by the compiler, although that depends on exactly what it is that you're writing.)
Your data will be laid out contiguously in memory, rather than every one of the different vectors allocating potentially disparate heap locations.
Having said that - #pasbi's answer is correct: You can use std::vector::reserve() to allocate space for your outer vector before inserting any actual elements (both for vectors-of-vectors and for vectors-of-arrays). Also, later on, you can use the std::vector::shrink_to_fit() method if you ended up inserting a lot less than you had planned.
Finally, one other option is to use a gsl::multispan and pre-allocate memory for it (GSL is the C++ Core Guidelines Support Library).
You've already answered your own question.
There is a function vector::reserve which does exactly what you want.
vector<vector<int>> a;
a.reserve(N);
for(some loop){
...
a.push_back(vector<int>(4){val1,val2,val3,val4});
}
This will reserve memory to fit N times vector<int>. Note that the actual size of the inner vector<int> is irrelevant at this point since the data of a vector is allocated somewhere else, only a pointer and some bookkeeping is stored in the actual std::vector-class.
Note: this answer is only here for completeness in case you ever come to have a similar problem with an unknown size; keeping a std::vector<std::array<int, 4>> in your case will do perfectly fine.
To pick up on einpoklum's answer, and in case you didn't find this earlier, it is almost always a bad idea to have nested std::vectors, because of the memory layout he spoke of. Each inner vector will allocate its own chunk of data, which won't (necessarily) be contiguous with the others, which will produce cache misses.
Preferably, either:
Like already said, use an std::array if you have a fixed and known amount of elements per vector;
Or flatten your data structure by having a single std::vector<T> of size N x M.
// Assuming N = 13000, M = 4
std::vector<int> vec;
vec.reserve(13000 * 4);
Then you can access it like so:
// Before:
int& element = vec[nIndex][mIndex];
// After:
int& element = vec[mIndex * 13000 + nIndex]; // Still assuming N = 13000
I am making a game engine and need to use the std::vector container for all of the components and entities in the game.
In a script the user might need to hold a pointer to an entity or component, perhaps to continuously check some kind of state. If something is added to the vector that the pointer points to and the capacity is exceeded, it is my understanding that the vector will allocate new memory and every pointer that points to any element in the vector will become invalid.
Considering this issue i have a couple of possible solutions. After each push_back to the vector, would it be a viable to check if a current capacity variable is exceeded by the actual capacity of the vector? And if so, fetch and overwrite the old pointers to the new ones? Would this guarantee to "catch" every case that invalidates pointers when performing a push_back?
Another solution that i've found is to instead save an index to the element and access it that way, but i suspect that is bad for performance when you need to continuously check the state of that element (every 1/60 second).
I am aware that other containers do not have this issue but i'd really like to make it work with a vector. Also it might be worth noting that i do not know in advance how many entities / components there will be.
Any input is greatly appreciated.
You shouldn't worry about performance of std::vector when you access its element only 60 times per second. By the way, in Release compilation mode std::vector::operator[] is being converted to a single lea opcode. In Debug mode it is decorated by some runtime range checks though.
If the user is going to store pointers to the objects, why even contain them in a vector?
I don't feel like it is a good idea to (poor wording)->store pointers to objects in a vector. (what I meant is to create pointers that point to vector elements, i.e. my_ptr = &my_vec[n];) The whole point of a container is to reference the contents in the normal ways that the container supports, not to create outside pointers to elements of the container.
To answer your question about whether you can detect the allocations, yes you could, but it is still probably a bad idea to reference the contents of a vector by pointers to elements.
You could also reserve space in the vector when you create it, if you have some idea of what the maximum size might grow to. Then it would never resize.
edit:
After reading other responses, and thinking about what you asked, another thought occurred. If your vector is a vector of pointers to objects, and you pass out the pointers to the objects to your clients, resizing the vector does not invalidate the pointers that the vector hold. The issue becomes keeping track of the life of the object (who owns it), which is why using shared_ptr would be useful.
For example:
vector<shared_ptr> my_vec;
my_vec.push_back(stuff);
if you pass out the pointers contained in the vector to clients...
client_ptr = my_vec[3];
There will be no problem when the vector resizes. The contents of the vector will be preserved, and whatever was at my_vec[3] will still be there. The object pointed to by my_vec[3] will still be at the same address, and my_vec[3] will still contain that address. Whomever got a copy of the pointer at my_vec[3] will still have a valid pointer.
However, if you did this:
client_ptr = &my_vec[3];
And the client is dereferencing like this:
*client_ptr->whatever();
You have a problem. Now when my_vec resized, &my_vec[3] is probably no longer valid, thus client_ptr points to nowhere.
If something is added to the vector that the pointer points to and the
capacity is exceeded, it is my understanding that the vector will
allocate new memory and every pointer that points to any element in
the vector will become invalid.
I once wrote some code to analyze what happens when a vector's capacity is exceeded. (Have you done this, yet?) What that code demonstrated on my Ubuntu with g++v5 system was that std::vector code simply a) doubles the capacity, b) moves all the elements from old to the new storage, then c) cleans up the old. Perhaps your implementation is similar. I think the details of capacity expansion is implementation dependent.
And yes, any pointer into the vector would be invalidated when push_back() causes capacity to be exceeded.
1) I simply don't use pointers-into-the-vector (and neither should you). In this way the issue is completely eliminated, as it simply can not occur. (see also, dangling pointers) The proper way to access a std::vector (or a std::array) element is to use an index (via the operator[]() method).
After any capacity-expansion, the index of all elements at indexes less than the previous capacity limit are still valid, as the push_back() installed the new element at the 'end' (I think highest memory addressed.) The elements memory location may have changed, but the element index is still the same.
2) It is my practice that I simply don't exceed the capacity. Yes, by that I mean that I have been able to formulate all my problems such that I know the required maximum-capacity. I have never found this approach to be a problem.
3) If the vector contents can not be contained in system memory (my system's best upper limit capacity is roughly 3.5 GBytes), then perhaps a vector container (or any ram based container) is inappropriate. You will have to accomplish your goal using disk storage, perhaps with vector containers acting as a cache.
update 2017-July-31
Some code to consider from my latest Game of Life.
Each Cell_t (on the 2-d gameboard) has 8 neighbors.
In my implementation, each Cell_t has a neighbor 'list,' (either std::array or std::vector, I've tried both), and after the gameboard has fully constructed, each Cell_t's init() method is run, filling it's neighbor 'list'.
// see Cell_t data attributes
std::array<int, 8> m_neighbors;
// ...
void Cell_t::void init()
{
int i = 0;
m_neighbors[i] = validCellIndx(m_row-1, m_col-1); // 1 - up left
m_neighbors[++i] = validCellIndx(m_row-1, m_col); // 2 - up
m_neighbors[++i] = validCellIndx(m_row-1, m_col+1); // 3 - up right
m_neighbors[++i] = validCellIndx(m_row, m_col+1); // 4 - right
m_neighbors[++i] = validCellIndx(m_row+1, m_col+1); // 5 - down right
m_neighbors[++i] = validCellIndx(m_row+1, m_col); // 6 - down
m_neighbors[++i] = validCellIndx(m_row+1, m_col-1); // 7 - down left
m_neighbors[++i] = validCellIndx(m_row, m_col-1); // 8 - left
// ^^^^^^^^^^^^^- returns info to quickly find cell
}
The int value in m_neighbors[i] is the index into the gameboard vector. To determine the next state of the cell, the code 'counts the neighbor's states.'
Note - Some cells are at the edge of the gameboard ... in this implementation, validCellIndx() can return a value indicating 'no-neighbor', (above top row, left of left edge, etc.)
// multiplier: for 100x200 cells,20,000 * m_generation => ~20,000,000 ops
void countNeighbors(int& aliveNeighbors, int& totalNeighbors)
{
{ /* ... initialize m_count[]s to 0 */ }
for(auto neighborIndx : m_neighbors ) { // each of 8 neighbors // 123
if(no_neighbor != neighborIndx) // 8-4
m_count[ gBoard[neighborIndx].m_state ] += 1; // 765
}
aliveNeighbors = m_count[ CellALIVE ]; // CellDEAD = 1, CellALIVE
totalNeighbors = aliveNeighbors + m_count [ CellDEAD ];
} // Cell_Arr_t::countNeighbors
init() pre-computes the index to this cells neighbors. The m_neighbors array holds index integers, not pointers. It is trivial to have NO pointers-into-the-gameboard vector.
case 1:
std::vector< Ticker > snap_tickers_ (n_instruments);
and
case 2:
std::vector< Ticker >snap_tickers_;
snap_tickers_.resize(n_instruments);
I am getting a compilation error when am trying case 2, whereas in case 1 am not getting any build failure. Can that be related to the type of object for which the vector is created?
ANSWER:
resize in case 2 makes use of copy constructor, which was deleted for Ticker class, hence the failure.
There is no real difference.
case 1:
std::vector<int> vec(5);
allocates 5 int-elements.
case2:
std::vector<int> vec;
vec.resize(5);
here, we begin with an empty vector of ints.
When you then call resize, the function checks if the size you passed over is smaller than the actual size (wich is 0, in that case). If yes, allocate _Newsize - size() new elements. If no, pop_back (delete) size() - _Newsize elements.
So in the end, resize is slower, because there are more machine cycles (if statements, subtracting sizes...) to do.
if you want to now more, here's the resize function from vector:
void resize(size_type _Newsize)
{ // determine new length, padding as needed
if (_Newsize < size())
_Pop_back_n(size() - _Newsize);
else if (size() < _Newsize)
{ // pad as needed
_Alty _Alval(this->_Getal());
_Reserve(_Newsize - size());
_TRY_BEGIN
_Uninitialized_default_fill_n(this->_Mylast, _Newsize - size(),
_Alval);
_CATCH_ALL
_Tidy();
_RERAISE;
_CATCH_END
this->_Mylast += _Newsize - size();
}
}
as you can see, it does quite a lot.
But in the end, it's just a question about (in most cases not important) micro-seconds...
So no real difference.
According to the C++ standard (since C++03), std::vector is required to store all elements contiguously,
[...] which means that elements can be accessed not only through iterators, but also using offsets on regular pointers to elements. This means that a pointer to an element of a vector may be passed to any function that expects a pointer to an element of an array.
Because of this restriction, resizing can potentially slow down performance because of the necessity of copying elements over to a new preallocated block. In practice, this overhead is usually only seen when resizing existing vectors with a lot of items requiring the vector to copy (or move) all of the objects to a new memory location.
In the example you gave, there is no real difference because the original vector had no items in it (and many compilers pre-allocate a chunk of memory to begin). I wouldn't be surprised if the compiler did an optimization to render equivalent code.