Why not resize and clear works in GotW 54? - c++

Referring to article Gotw 54 by HerbSutter, he explains about
The Right Way To "Shrink-To-Fit" a
vector or deque and
The Right Way to Completely Clear a vector or
deque
Can we just use container.resize()
and container.clear() for the above task
or am I missing something?

There are two different things that a vector holds: size Vs capacity. If you just resize the vector, there is no guarantee that the capacity(how much memory is reserved) must change. resize is an operation concerned with how much are you using, not how much the vector capacity is.
So for example.
size == how much you are using
capacity == how much memory is reserved
vector<int> v(10);
v.resize(5); // size == 5 but capacity (may or may) not be changed
v.clear() // size == 0 but capacity (may or may) not be changed
In the end, capacity should not changed on every operation, because that would bring a lot of memory allocation/deallocation overhead. He is saying that if you need to "deallocate" the memory reserved by vector, do that.

Neither resize() nor clear() work. The .capacity() of a vector is guaranteed to be at least as big as the current size() of the vector, and guaranteed to be at least as big as the reserve()d capacity. Also, this .capacity() doesn't shrink, so it is also at least as big as any previous size() or reserve()ation.
Now, the .capacity() of a vector is merely the memory it has reserved. Often not all of that memory cotains objects. Resizing removes objects, but doesn't recycle the memory. A vector can only recycle its memory buffer when allocating a larger buffer.
The swap trick works by copying all ojects to a smaller, more appropriate memory buffer. Afterwards, the original memory buffer can be recycled. This appears to violate the previous statement that the memory buffer of a vector can only grow. However, with the swap trick, you temporarily have 2 vectors.

The vector has size and capacity. It may hold X elements but have uninitialized memory in store for Y elements more. In a typical implementation erase, resize (when resizing to smaller size) and clear don't affect the capacity: the vector keeps the memory around for itself, should you want to add new items to it at a later time.

Related

Difference between std::resize(n) and std::shrink_to_fit in C++?

I came across these statements:
resize(n) – Resizes the container so that it contains ‘n’ elements.
shrink_to_fit() – Reduces the capacity of the container to fit its size and destroys all elements beyond the capacity.
Is there any significant difference between these functions? they come under vectors in c++
Vectors have two "length" attributes that mean different things:
size is the number of usable elements in the vector. It is the number of things you have stored. This is a conceptual length.
capacity is how many elements would fit into the amount of memory the vector has currently allocated.
capacity >= size must always be true, but there is no reason for them to be always equal. For example, when you remove an element, shrinking the allocation would require creating a new allocation one bucket smaller and moving the remaining contents over ("allocate, move, free").
Similarly, if capacity == size and you add an element, the vector could grow the allocation by one element (another "allocate, move, free" operation), but usually you're going to add more than one element. If the capacity needs to increase, the vector will increase its capacity by more than one element so that you can add several more elements before needing to move everything again.
With this knowledge, we can answer your question:
std::vector<T>::resize() changes the size of the array. If you resize it smaller than its current size, the excess objects are destructed. If you resize it larger than its current size, the "new" objects added at the end are default-initialized.
std::vector<T>::shrink_to_fit() asks for the capacity to be changed to match the current size. (Implementations may or may not honor this request. They might decrease the capacity but not make it equal to the size. They might not do anything at all.) If the request is fulfilled, this will discard some or all of the unused portion of the vector's allocation. You'd typically use this when you are done building a vector and will never add another item to it. (If you know in advance how many items you will be adding, it would be better to use std::vector<T>::reserve() to tell the vector before adding any items instead of relying on shrink_to_fit doing anything.)
So you use resize() to change how much stuff is conceptually in the vector.
You use shrink_to_fit() to minimize the excess space the vector has allocated internally without changing how much stuff is conceptually in the vector.
shrink_to_fit() – Reduces the capacity of the container to fit its size and destroys all elements beyond the capacity.
That is a mischaracterization of what happens. Secifically, the destroys all elements beyond the capacity part is not accurate.
In C++, when dynamically memory is used for objects, there are two steps:
Memory is allocated for objects.
Objects are initialized/constructed at the memory locations.
When objects in dynamically allocated memory are deleted, there are also two steps, which mirror the steps of construction but in inverse order:
Objects at the memory locations destructed (for built-in types, this is a noop).
Memory used by the objects is deallocated.
The memory allocated beyond the size of the container is just buffer. They don't hold any properly initialized objects. It's just raw memory. shrink_to_fit() makes sure that the additional memory is not there but there were no objects in those locations. Hence, nothing is destroyed, only memory is deallocated.
According to the C++ Standard relative to shrink_to_fit
Effects: shrink_to_fit is a non-binding request to reduce capacity()
to size().
and relative to resize
Effects: If sz < size(), erases the last size() - sz elements from the
sequence. Otherwise, appends sz - size() default-inserted elements to
the sequence.
It is evident that the functions do different things. Moreover the first function has no parameter while the second function has even two parameters. The function shrink_to_fit does not change the size of the container though can reallocate memory.

What does 'compacting memory' mean when removing items from the front of a std::vector?

Remove first N elements from a std::vector
This question talks about removing items from a vector and 'compacting memory'. What is 'compacting memory' and why is it important here?
Inside the implementation of the std::vector class is some code that dynamically allocates an array of data-elements. Often not all of the elements in this internal array will be in use -- the array is often allocated to be bigger than what is currently needed, in order to avoid having to reallocate a bigger array too often (array-reallocations are expensive!).
Similarly, when items are removed from the std::vector, the internal data-array is not immediately reallocated to be smaller (because doing so would be expensive); rather, the now-extra slots in the array are left "empty" in the expectation that the calling code might want to re-use them in the near future.
However, those empty slots still take up RAM, so if the calling code has just removed a lot of items from the vector, it might want to force the vector to reallocate a smaller internal array that doesn't contain so many empty slots. That is what they are referring to as compacting in that question.
The OP is talking about shrinking the memory the vector takes up. When you erase elements from a vector its size decreases but it capacity (the memory it is using) remains the same. When the OP says
(that also compacts memory)
They want the removal of the elements to also shrink the capacity of the vector so it reduces its memory consumption.
It means that the vector shouldn't use more memory than it needs to. In other words, the OP wants:
size() == capacity()
This can be achieved in C++11 and later by calling shrink_to_fit() on a vector. This is only a request though, it is not binding.
To make sure it will compact memory you should create a new vector and call reserve(oldvector.size()).

Does resize() to a smaller size discard the reservation made by earlier reserve()?

So if I reserve(100) first, add some elements, and then resize(0) (or any other number smaller than the current size), will the vector reallocate memory to a take less space than 100 elements?
vector<T>::resize(0) should not cause a reallocation or deletion of allocated memory, and for this reason in most cases is preferable to vector<T>::clear().
For more detail see answers to this question: std::vector resize downward
Doing a vector::resize(0) or to a smaller count rather than current count should not deallocate any memory. However, it may destroy these elements.
For a reference on std::vector::resize, take a look at std::vector::resize

C++ Vector Memory Usage - Is it ever freed?

I know that vectors double in size whenever their capacity() is exceeded. This operation takes some time which is why vectors are supposed to have amortized constant time for addition of elements with push_back().
What I'm wondering is... what happens when a vector shrinks so that its size() is less than half of the capacity().
Do vectors ever relinquish the memory which they use, or is it just gone until the vector is destroyed?
It could be a lot of wasted memory if they don't shrink in size ever, but I've never heard of them having that feature.
No, it is never freed (i.e. capacity is never reduced) until destruction. A common idiom for freeing up some memory is to create a new vector of the correct size, and use that instead:
std::vector<type>(originalVector).swap(originalVector);
(inspired by "More Exceptional C++", Item #7)
If you want to make sure your vector uses as little space as possible, you can say:
std::vector<Foo>(my_vector).swap(my_vector);
You could also call the shrink_to_fit() member function, but that is just a non-binding request.
Erasing elements from a vector or even clearing the vector entirely does not necessarily free any of the memory associated with that element. This is because the maximum size of a vector since its creation is a good estimate of the future size of the vector.
To use the shrink to fit idiom:
std::vector<int> v;
//v is swapped with its temporary copy, which is capacity optimal
std::vector<int>(v).swap(v);
Solution in C++0x
In C++0x some containers declare such idiom as function shrink_to_fit(), e.g. vector, deque, basic_string. shrink_to_fit() is a non-binding request to reduce capacity() to size().
They don't shrink in size.
It would not be generally useful to de-allocate the memory.
For example, a vector could shrink now but then grow again later. It would be a waste to deallocate the extra memory (and copy all the elements in the memory) only to reallocate it later.
If a vector is shrinking, there is also a good chance the vector will be shrunk to empty and then destroyed. In that case deallocating as it shrinks would be a waste of time as well.
It can also be a useful optimization to reuse a vector object so as to avoid allocating/dealocating the contents. That would be ruined by introducing deallocations as it shrinks.
Basically, most of the time deallocating the extra memory isn't worth it. In the few case where it is, you can specifically ask for dealocation.
The memory is gone until the vector is destroyed.

Changing the reserve memory of C++ vector

I have a vector with 1000 "nodes"
if(count + 1 > m_listItems.capacity())
m_listItems.reserve(count + 100);
The problem is I also clear it out when I'm about to refill it.
m_listItems.clear();
The capacity doesn't change.
I've used the resize(1); but that doesn't seem to alter the capacity.
So how does one change the reserve?
vector<Item>(m_listItems).swap(m_listItems);
will shrink m_listItems again: http://www.gotw.ca/gotw/054.htm (Herb Sutter)
If you want to clear it anyway, swap with an empty vector:
vector<Item>().swap(m_listItems);
which of course is way more efficient. (Note that swapping vectors basicially means just swapping two pointers. Nothing really time consuming going on)
You can swap the vector as others have suggested, and as described in http://www.gotw.ca/gotw/054.htm but be aware that it is not free, you're performing a copy of every element, because the vector has to allocate a new, smaller, chunk of memory, and copy all the old contents over. (The swap operation is essentially free, but you're swapping with a temporary initialized with a copy of the original vector's data, which is not free)
If you know in advance how big the vector is, you should allocate the right size to begin with, so no resizing is necessary:
std::vector<foo> v(1000); // Create a vector with capacity for 1000 elements
And if you don't know the capacity in advance, why does it matter whether it wastes a bit of space? Is it worth the time spent copying every element to a new and smaller vector (which is what std::vector(v).swap(v) will do), just to save a few kilobytes of memory?
Similarly, when you clear the vector, if you intend to refill it anyway, setting its capacity to zero seems to be an impressive waste of time.
Edit:
baash05: what if you had 1000000 items
an 10 megs of ram. would you say
reducing the amount of overhead is
important?
No. Resizing the vector requires more memory, temporarily, so if you're memory-limited, that might break your app. (You have to have the original vector in memory, and the temporary, before you can swap them, so you end up using up to twice as much RAM at that point). Afterwards, you might save a small amount of memory (up to a couple of MB), but this doesn't matter, because the excess capacity in the vector would never be accessed, so it would get pushed to the pagefile, and so not count towards your RAM limit in the first place.
If you have 1000000 items, then you should initialize the vector to the correct size in the first place.
And if you can't do that, then you'll typically be better off leaving the capacity alone. Especially since you stated that you're going to refill the vector, you should definitely reuse the capacity that has already been allocated, rather than allocating, reallocating, copying and freeing everything constantly.
You have two possible cases. Either you know how many elements you need to store, or you don't. If you know, then you can create the vector with the correct size in the first place, and so you never need to resize it, or you don't know, and then you might as well keep the excess capacity, so at least it won't have to resize upwards when you refill your vector.
You could try this technique from here
std::vector< int > v;
// ... fill v with stuff...
std::vector< int >().swap( v );
You can swap it with a new vector that has desired capacity.
vector< int > tmp;
old.swap( tmp );
As far as I can tell, you can't reallocate a vector to a lower capacity than it ever has; you can only allocate it larger. There are good reasons for this; among them is that the reallocation process is hugely computationally intensive. If you really need to have a smaller vector, free the old one and create a new one that's smaller. That's actually computationally much simpler than having the vector actually resize smaller.