17

I'm merging many objects into a single vector containing render data (a mesh). This vector gets cleared and refilled on each frame (well, almost).

The issue is that clearing and then again reserving the vector size has a huge impact on performance in my case, because clear() may also change the capacity.

In other words, I need to control when the capacity of the vector gets changed. I want to keep the old capacity for quite some time, until I decide myself that it's time to change it.

I see two options:

  • Figure out how to control when the capacity of std::vector is about to change
  • Implement a memory pool for large memory objects which will fetch a large data object of <= required size and reuse / release it as I need.

Update

In addition, what if for example a resize(10), and later a resize(5) was called (just for illustration, multiply actual numbers by some millions)?

Will the later call to resize(5) cause the vector to, maybe, reallocate?

Azeem
  • 11,148
  • 4
  • 27
  • 40
benjist
  • 2,740
  • 3
  • 31
  • 58
  • 8
    Why do you think that `.resize(0)` would change the capacity? [std::vector::resize](http://en.cppreference.com/w/cpp/container/vector/resize) `[...]Vector capacity is never reduced when resizing to smaller size because that would invalidate all iterators, rather than only the ones that would be invalidated by the equivalent sequence of pop_back() calls.[...]` The same is with [std::vector::clear](http://en.cppreference.com/w/cpp/container/vector/clear) – t.niese Mar 16 '17 at 17:43
  • 1
    vector `resize` or `clear` are never allowed to reduce the capacity. Only way to do that is `swap` or `shrink_to_fit`. – sbabbi Mar 16 '17 at 17:44
  • @sbabbi Why can't clear reduce the capacity? – NathanOliver Mar 16 '17 at 17:46
  • 1
    @NathanOliver From [`std::vector::clear`](http://en.cppreference.com/w/cpp/container/vector/clear) : *"Leaves the capacity() of the vector unchanged (note: the standard's restriction on the changes to capacity is in the specification of vector::reserve, see [1])"* [Note 1](http://stackoverflow.com/questions/18467624/what-does-the-standard-say-about-how-calling-clear-on-a-vector-changes-the-capac/18467916#18467916) – François Andrieux Mar 16 '17 at 17:56
  • Thanks for the comments. I was assuming this because the ref. (http://www.cplusplus.com/reference/vector/vector/clear/) says: "A reallocation is not guaranteed to happen, and the vector capacity is not guaranteed to change due to calling this function[...]". This is not the same as saying capacity never changes. – benjist Mar 16 '17 at 17:57
  • @FrançoisAndrieux Thanks man. Good read. – NathanOliver Mar 16 '17 at 17:58
  • Might also read [this](http://stackoverflow.com/questions/9521629/stdstringss-capacity-reserve-resize-functions) for reserve(),capacity() and resize() – zar Mar 16 '17 at 18:24
  • 1
    @Benjist Use cppreference next time. – Hatted Rooster Mar 16 '17 at 22:36
  • 1
    @benjist cplusplus.com is a poor site with many errors – M.M Mar 16 '17 at 22:57
  • @FrançoisAndrieux That logic is unconvincing. The same argument also implies that `shrink_to_fit` and `swap` are not allowed to reduce the capacity. – M.M Mar 16 '17 at 23:00
  • @M.M "`shrink_­to_­fit` is a non-binding request to reduce `capacity()` to `size()`" "`swap(vector & x)` Exchanges the contents and `capacity()` of `*this` with that of `x`" [`[vector.capacity]`](http://eel.is/c++draft/vector.capacity) – Caleth Sep 12 '19 at 09:37
  • @Caleth yes, hence my comment – M.M Sep 12 '19 at 14:12

2 Answers2

19

Actually the clear member function keeps the vector capacity unchanged. It only destroys (calls the destructor) each of the vector elements and sets the vector size to 0.

In this situation, at each iteration, I would call clear() to destroy all the vector elements, then call the member function reserve(size) which, in the case where the vector capacity is too small, will increase it to at least size.

Oliv
  • 17,610
  • 1
  • 29
  • 72
1

This vector gets cleared and refilled on each frame (well, almost).

I would recommend a different approach.

Create a class that acts as a buffer for the rendering data.

If I am not mistaken, you never reduce the capacity of the buffer. You only increase its capacity when needed.

Make sure that class is an implementation detail and only instance is ever constructed.

Here's a skeletal implementation of what I am thinking.

namespace Impl_Detail
{
   struct Buffer
   {
      size_t capacity;
      size_t size;
      std::vector<char> data;

      // Pick whatever default capacity makes sense for your need.
      Buffer(size_t cap = 100) : capacity_(cap), size_(0), data(cap) {}

      void ensureCapacity(size_t cap)
      {
         if ( capacity_ < cap )
         {
            capacity_ = cap;
            data.resize(capacity_);
         }
      }


      // Add any other helpful member functions as needed.
   };

   // Create an instance and use it in the implementation.
   Buffer theBuffer;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • also, with this approach, make a variable "int amountUsed", and instead of "clearing" the array, you just set "amountUsed" to zero, and overwrite the existing values as needed. Just overwrite the data and ignore everything after the "amountUsed" index. – Jason Lang Mar 16 '17 at 17:54
  • @JasonLang, The purpose of `size` is to serve that role. – R Sahu Mar 16 '17 at 17:56
  • 4
    This all seems a bit unnecessary. Why not just use std::vector::reserve()? – user673679 Mar 16 '17 at 18:35
  • Because you can't control the memory yourself that way. A useful performance addition we've used is to make that test expected to be false. So that'd be `if (unlikely(capacity_ < cap))`. A specialized arena allocator would then call the ensure size thing, but it'd usually be done with exponential growth, not just resizing up to given capacity. Also, at this point, you can control where the memory is, too, rather than just letting that `std::vector` allocate on the heap. – Bob Carpenter Jul 31 '18 at 09:44