views:

302

answers:

2

I would like to 'shrink-to-fit' an std::vector, to reduce its capacity to its exact size, so that additional memory is freed. The standard trick seems to be the one described here:

template< typename T, class Allocator >
void shrink_capacity(std::vector<T,Allocator>& v)
{
   std::vector<T,Allocator>(v.begin(),v.end()).swap(v);
}

The whole point of shrink-to-fit is to save memory, but doesn't this method first create a deep copy and then swaps the instances? So at some point -- when the copy is constructed -- the memory usage is doubled?

If that is the case, is there a more memory-friendly method of shrink-to-fit? (In my case the vector is really big and I cannot afford to have both the original plus a copy of it in memory at any time.)

+1  A: 

Well, if you wanted to resize an array, what would you do? You have to create a new one and copy all of the values over - be it individually or with memcpy or whatever. You can't really resize an array in C or C++.

std::vector is pretty much guaranteed to be implemented using an array for its storage (IIRC, the standard does not guarantee that it's an array, but an array is the only thing which can fulfill the various requirements of the API such as how efficient each operation must be; so, in effect, it's guaranteed even if that guarantee isn't explicit). As it's implemented using an array, and you can't resize arrays without copying, you can't resize vectors without copying.

You could, in theory, have a shrink_capacity() function which hid the fact that you had to temporarily more or less double its size requirements, but since std::vector does not currently have such a function, you have to actually make an explicit copy. The swap trick is just a nice way to do that.

If you really care about memory in such a case, what you can do is use pointers (or smart pointers) instead of having the vector hold the objects directly. That may not be entirely desirable, but it would reduce your memory requirements.

Jonathan M Davis
why can't stl::Vector simply tell malloc (or new) that it no longer needs the memory from the final entry to the reserved size?Then the heap allocator can just put it back on the free list - no need to copy anything
Martin Beckett
@Martin I've never heard of any way to do that. There's realloc, but since that doesn't have any guarantee that it's going to return the same block of memory, you lose whatever was in the array before. If there is a way to do that, then I assume that vector could do that, but it doesn't currently have such a function even it's possible.
Jonathan M Davis
Because most memory allocators don't work that way; you cannot edit the boundary tag of an already allocated bloc to make it smaller in place while yielding the slack back to the free list. Some exotic allocators can do this, but the standard library isn't designed to depend on them.
Crashworks
No I haven't seen any that can do this - I'm just pointing out that you don't theoretically NEED to copy memory to free spare space at the end of a vector. It's why people working in HPC often end up managing their own memory.
Martin Beckett
A: 

If your new size is half of the original, you might be able to get away with placement newing your new vector (or a straight dynaimc array if vector can't do this) into the unused end portion of your old one. Not sure if vector stores info in that area of memory so this would be very hacky and scary. But it's an idea.

Now that I think about it, a memMove() type operation where you copy the information in reverse from the last index used in the original to the back of the unused area in the original would preserve the data. If you made this a placement new of an array you could point it to wherever the new data would have existed in the middle on the original memory area. An in place move per se.

Michael Dorgan