void-pointer
void-pointer

Reputation: 14827

Efficient Array Reallocation in C++

How would I efficiently resize an array allocated using some standards-conforming C++ allocator? I know that no facilities for reallocation are provided in the C++ alloctor interface, but did the C++11 revision enable us to work with them more easily? Suppose that I have a class vec with a copy-assignment operator foo& operator=(const foo& x) defined. If x.size() > this->size(), I'm forced to

  1. Call allocator.destroy() on all elements in the internal storage of foo.
  2. Call allocator.deallocate() on the internal storage of foo.
  3. Reallocate a new buffer with enough room for x.size() elements.
  4. Use std::uninitialized_copy to populate the storage.

Is there some way that I more easily reallocate the internal storage of foo without having to go through all of this? I could provide an actual code sample if you think that it would be useful, but I feel that it would be unnecessary here.

Upvotes: 12

Views: 2064

Answers (6)

void-pointer
void-pointer

Reputation: 14827

Interestingly, the default allocator for g++ is smart enough to use the same address for consecutive deallocations and allocations of larger sizes, as long as there is enough unused space after the end of the initially-allocated buffer. While I haven't tested what I'm about to claim, I doubt that there is much of a time difference between malloc/realloc and allocate/deallocate/allocate.

This leads to a potentially very dangerous, nonstandard shortcut that may work if you know that there is enough room after the current buffer so that a reallocation would not result in a new address. (1) Deallocate the current buffer without calling alloc.destroy() (2) Allocate a new, larger buffer and check the returned address (3) If the new address equals the old address, proceed happily; otherwise, you lost your data (4) Call allocator.construct() for elements in the newly-allocated space.

I wouldn't advocate using this for anything other than satisfying your own curiosity, but it does work on g++ 4.6.

Upvotes: 0

MSN
MSN

Reputation: 54604

There is no function that will resize in place or return 0 on failure (to resize). I don't know of any operating system that supports that kind of functionality beyond telling you how big a particular allocation actually is.

All operating systems do however have support for implementing realloc, however, that does a copy if it cannot resize in place.

So, you can't have it because the C++ language would not be implementable on most current operating systems if you had to add a standard function to do it.

Upvotes: 1

Chang
Chang

Reputation: 4133

Even if re-allocate exists, actually, you can only avoid #2 you mentioned in your question in a copy constructor. However in the case of internal buffer growing, re-allocate can save these four operations.

  1. Is internal buffer of your array continuous? if so see the answer of your link
  2. if not, Hashed array tree or array list may be your choice to avoid re-allocate.

Upvotes: 0

SmacL
SmacL

Reputation: 22922

Based on a previous question, the approach that I took for handling large arrays that could grow and shrink with reasonable efficiency was to write a container similar to a deque that broke the array down into multiple pages of smaller arrays. So for example, say we have an array of n elements, we select a page size p, and create 1 + n/p arrays (pages) of p elements. When we want to re-allocate and grow, we simply leave the existing pages where they are, and allocate the new pages. When we want to shrink, we free the totally empty pages.

The downside is the array access is slightly slower, in that given and index i, you need the page = i / p, and the offset into the page i % p, to get the element. I find this is still very fast however and provides a good solution. Theoretically, std::deque should do something very similar, but for the cases I tried with large arrays it was very slow. See comments and notes on the linked question for more details.

There is also a memory inefficiency in that given n elements, we are always holding p - n % p elements in reserve. i.e. we only ever allocate or deallocate complete pages. This was the best solution I could come up with in the context of large arrays with the requirement for re-sizing and fast access, while I don't doubt there are better solutions I'd love to see them.

Upvotes: 4

David C. Bishop
David C. Bishop

Reputation: 6615

There are the C++11 rvalue reference and move constructors.

There's a great video talk on them.

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283634

A similar problem also arises if x.size() > this->size() in foo& operator=(foo&& x).

No, it doesn't. You just swap.

Upvotes: 1

Related Questions