Boost logo

Boost Users :

Subject: Re: [Boost-users] multi_index reserve but allocate
From: Olivier Tristan (o.tristan_at_[hidden])
Date: 2019-03-20 13:06:55


Le 20/03/2019 à 11:18, Joaquin M López Muñoz via Boost-users a écrit :
> Maybe you can give this allocator a try:
>
> https://probablydance.com/2014/11/09/plalloc-a-simple-stateful-allocator-for-node-based-containers/
>
>
> It works with Boost.MultiIndex and does not use any mutex at all
> (because it's stateful and
> as such instances are owned by the containers using them).
>
I tried using directly boost::pool but I was struggling having an
allocator that actually compiled (last issue was related to swap)

This is a great example to start from.

In the meantime, I've found as well info regarding
boost::container::node_allocator but the memory is shared across
instances and do not allow preallocation.

Didn't found any other out of the box solution in boost though

so I've ended up modifying the example.

Still I need to do a reserve on multi_index to force the creation of all
the allocator

Thanks a lot !

For those interested, here is what I end up with

 Â  template <typename T>
 Â  struct PoolAlloc
 Â  {
 Â Â Â  typedef T value_type;

 Â Â Â  PoolAlloc(size_t reserveSize)
 Â Â Â Â Â  : reservedSize(reserveSize)
 Â Â Â  {
 Â Â Â Â Â  reserve(reserveSize);
 Â Â Â  }
 Â Â Â  template <typename U>
 Â Â Â  PoolAlloc(const PoolAlloc<U>& other)
 Â Â Â Â Â  : reservedSize(other.reservedSize)
 Â Â Â  {
 Â Â Â Â Â  reserve(reservedSize);
 Â Â Â  }
 Â Â Â  PoolAlloc(const PoolAlloc&) = delete;
 Â Â Â  PoolAlloc& operator=(const PoolAlloc&) = delete;
 Â Â Â  PoolAlloc(PoolAlloc&&)                 = default;
 Â Â Â  PoolAlloc& operator=(PoolAlloc&&) = default;

 Â Â Â  typedef std::true_type propagate_on_container_copy_assignment;
 Â Â Â  typedef std::true_type propagate_on_container_move_assignment;
 Â Â Â  typedef std::true_type propagate_on_container_swap;

 Â Â Â  void reserve(size_t to_allocate)
 Â Â Â  {
 Â Â Â Â Â  available.reserve(to_allocate);
 Â Â Â Â Â  std::unique_ptr<value_holder[]> allocated(new
value_holder[to_allocate]);
 Â Â Â Â Â  value_holder*                   first_new = allocated.get();
 Â Â Â Â Â  memory.emplace_back(std::move(allocated));
 Â Â Â Â Â  for (size_t i = 0; i < to_allocate; ++i)
 Â Â Â Â Â  {
 Â Â Â Â Â Â Â  available.push_back(std::addressof(first_new[i].value));
 Â Â Â Â Â  }
 Â Â Â  }

 Â Â Â  bool operator==(const PoolAlloc& other) const
 Â Â Â  {
 Â Â Â Â Â  return this == &other;
 Â Â Â  }
 Â Â Â  bool operator!=(const PoolAlloc& other) const
 Â Â Â  {
 Â Â Â Â Â  return !(*this == other);
 Â Â Â  }

 Â Â Â  T* allocate(size_t num_to_allocate)
 Â Â Â  {
 Â Â Â Â Â  if (num_to_allocate != 1)
 Â Â Â Â Â  {
 Â Â Â Â Â Â Â  return static_cast<T*>(::operator new(sizeof(T) *
num_to_allocate));
 Â Â Â Â Â  }
 Â Â Â Â Â  else
 Â Â Â Â Â  {
 Â Â Â Â Â Â Â  if (available.empty())
 Â Â Â Â Â Â Â  {
 Â Â Â Â Â Â Â Â Â  // first allocate 8, then double whenever
 Â Â Â Â Â Â Â Â Â  // we run out of memory
 Â Â Â Â Â Â Â Â Â  size_t to_allocate = 8 << memory.size();
 Â Â Â Â Â Â Â Â Â  reserve(to_allocate);
 Â Â Â Â Â Â Â  }

 Â Â Â Â Â Â Â  T* result = available.back();
 Â Â Â Â Â Â Â  available.pop_back();
 Â Â Â Â Â Â Â  return result;
 Â Â Â Â Â  }
 Â Â Â  }

 Â Â Â  void deallocate(T* ptr, size_t num_to_free)
 Â Â Â  {
 Â Â Â Â Â  if (num_to_free == 1)
 Â Â Â Â Â  {
 Â Â Â Â Â Â Â  available.push_back(ptr);
 Â Â Â Â Â  }
 Â Â Â Â Â  else
 Â Â Â Â Â  {
 Â Â Â Â Â Â Â  ::operator delete(ptr);
 Â Â Â Â Â  }
 Â Â Â  }

 Â Â Â  // boilerplate that shouldn't be needed, except
 Â Â Â  // libstdc++ doesn't use allocator_traits yet
 Â Â Â  template <typename U>
 Â Â Â  struct rebind
 Â Â Â  {
 Â Â Â Â Â  typedef PoolAlloc<U> other;
 Â Â Â  };
 Â Â Â  typedef T*       pointer;
 Â Â Â  typedef const T* const_pointer;
 Â Â Â  typedef T&       reference;
 Â Â Â  typedef const T& const_reference;
 Â Â Â  template <typename U, typename... Args>
 Â Â Â  void construct(U* object, Args&&... args)
 Â Â Â  {
 Â Â Â Â Â  new (object) U(std::forward<Args>(args)...);
 Â Â Â  }
 Â Â Â  template <typename U, typename... Args>
 Â Â Â  void construct(const U* object, Args&&... args) = delete;
 Â Â Â  template <typename U>
 Â Â Â  void destroy(U* object)
 Â Â Â  {
 Â Â Â Â Â  object->~U();
 Â Â Â  }

 Â Â Â  union value_holder
 Â Â Â  {
 Â Â Â Â Â  value_holder() {}
 Â Â Â Â Â  ~value_holder() {}
 Â Â Â Â Â  T value;
 Â Â Â  };

 Â Â Â  size_t                                       reservedSize;
 Â Â Â  std::vector<std::unique_ptr<value_holder[]>> memory;
 Â Â Â  std::vector<T*>                              available;
 Â  };

-- 
Olivier Tristan
Research & Development
www.uvi.net

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net