LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
BulkAllocator.h
Go to the documentation of this file.
1 
8 //--- BEGIN issue #19494 -------------------------------------------------------
10 // We are leaving it here because, being a header, it will not bother unless
11 // explicitly invoked. Note that there is a unit test for it too.
12 #error("BulkAllocator.h is currently broken; see issue #19494.")
13 //--- END issue #19494 ---------------------------------------------------------
14 
15 #ifndef BULKALLOCATOR_H
16 #define BULKALLOCATOR_H
17 
18 // interface include
19 #include <cstdlib> // std::free
20 #include <memory> // std::allocator<>, std::unique_ptr<>
21 #include <stdexcept> // std::logic_error
22 
23 namespace lar {
25  namespace details {
27  namespace bulk_allocator {
28  template <typename T>
30  } // namespace bulk_allocator
31  } // namespace details
32 
34  class memory_error : public std::bad_alloc {
35  public:
36  memory_error() : std::bad_alloc() {}
37  memory_error(const char* message) : std::bad_alloc(), msg(message) {}
38 
39  virtual const char* what() const throw() override { return msg; }
40 
41  private:
42  const char* msg = nullptr;
43  }; // class memory_error
44 
88  template <typename T>
89  class BulkAllocator : public std::allocator<T> {
90  public:
91  using BaseAllocator_t = std::allocator<T>;
92 
93  // import types from the STL allocator
94  typedef typename BaseAllocator_t::size_type size_type;
95  typedef typename BaseAllocator_t::difference_type difference_type;
96 
97  typedef typename BaseAllocator_t::value_type value_type;
98 
99  typedef typename BaseAllocator_t::pointer pointer;
100  typedef typename BaseAllocator_t::const_pointer const_pointer;
101 
102  typedef typename BaseAllocator_t::reference reference;
103  typedef typename BaseAllocator_t::const_reference const_reference;
104 
105  template <typename U>
106  struct rebind {
108  };
109 
111  BulkAllocator() noexcept : BulkAllocator(GetChunkSize(), false) {}
112 
114  BulkAllocator(size_type ChunkSize, bool bPreallocate = false) noexcept
115  {
116  CreateGlobalAllocator(ChunkSize, bPreallocate);
117  }
118 
120  BulkAllocator(const BulkAllocator& a) noexcept = default;
121 
123  BulkAllocator(BulkAllocator&& a) noexcept = default;
124 
126  template <class U>
128  {
129  SetChunkSize(a.GetChunkSize());
130  }
131 
133  BulkAllocator& operator=(const BulkAllocator& a) = default;
134 
136  BulkAllocator& operator=(BulkAllocator&& a) = default;
137 
139  ~BulkAllocator() { GlobalAllocator.RemoveUser(); }
140 
142  pointer allocate(size_type n, const void* /* hint */ = 0);
143 
145  void deallocate(pointer p, size_type n);
146 
148  static void Free() { GlobalAllocator.Free(); }
149 
151  static size_type GetChunkSize() { return GlobalAllocator.GetChunkSize(); }
152 
154  static void SetChunkSize(size_type ChunkSize) { GlobalAllocator.SetChunkSize(ChunkSize); }
155 
156  private:
159 
161  void CreateGlobalAllocator(size_type ChunkSize, bool bPreallocate = false);
162 
165 
166  }; // class BulkAllocator<>
167 
168 } // namespace lar
169 
170 //------------------------------------------------------------------------------
171 #include <algorithm> // std::max()
172 #include <array>
173 #include <iostream>
174 #include <string>
175 #include <typeinfo>
176 #include <vector>
177 #ifdef __GNUG__
178 #include <cxxabi.h>
179 #endif // __GNUG__
180 
181 namespace lar {
182  namespace details {
184 
202  template <typename T>
203  std::string demangle()
204  {
205  std::string name = typeid(T).name();
206 #ifdef __GNUG__
207  int status; // some arbitrary value to eliminate the compiler warning
208  std::unique_ptr<char, void (*)(void*)> res{
209  abi::__cxa_demangle(name.c_str(), NULL, NULL, &status), std::free};
210  return (status == 0) ? res.get() : name;
211 #else // !__GNUG__
212  return name;
213 #endif // ?__GNUG__
214  } // demangle()
215 
216  template <typename T>
217  inline std::string demangle(const T&)
218  {
219  return demangle<T>();
220  }
222 
223  namespace bulk_allocator {
224  constexpr bool bDebug = false;
225 
228  public:
229  typedef unsigned int Counter_t;
230 
232  bool hasUsers() const { return counter > 0; }
233 
235  Counter_t Count() const { return counter; }
236 
238  void AddUser() { ++counter; }
239 
241  bool RemoveUser()
242  {
243  if (!counter) return false;
244  --counter;
245  return true;
246  }
247 
248  private:
249  Counter_t counter = 0;
250  }; // class ReferenceCounter
251 
264  template <typename T>
265  class BulkAllocatorBase : public ReferenceCounter {
266  public:
267  typedef size_t size_type;
268  typedef T value_type;
269  typedef T* pointer;
270 
272  BulkAllocatorBase(size_type NewChunkSize = DefaultChunkSize, bool bPreallocate = false);
273 
275  ~BulkAllocatorBase() { Free(); }
276 
278  void Free();
279 
281  pointer Get(size_type n);
282 
284  void Release(pointer) {}
285 
288 
290  void AddUser(size_type NewChunkSize, bool bPreallocate = false);
291 
293  bool RemoveUser();
294 
296  size_type AllocatedCount() const;
297 
299  size_type UsedCount() const;
300 
302  size_type FreeCount() const;
303 
305  size_type NChunks() const { return MemoryPool.size(); }
306 
308  std::array<size_type, 2> GetCounts() const;
309 
311  void SetChunkSize(size_type NewChunkSize, bool force = false);
312 
314  size_type GetChunkSize() const { return ChunkSize; }
315 
318  void Preallocate() { Preallocate(ChunkSize); }
319 
320  private:
321  typedef std::allocator<T> Allocator_t;
322  typedef typename Allocator_t::difference_type difference_type;
323 
324  Allocator_t allocator;
325 
328  public:
329  Allocator_t* allocator;
330 
331  pointer begin = nullptr;
332  pointer end = nullptr;
333  pointer free = nullptr;
334 
336  MemoryChunk_t(Allocator_t& alloc, size_type n) : allocator(&alloc)
337  {
338  begin = n ? allocator->allocate(n) : nullptr;
339  end = begin + n;
340  free = begin;
341  } // MemoryChunk_t()
342  MemoryChunk_t(const MemoryChunk_t&) = delete;
344 
345  ~MemoryChunk_t() { allocator->deallocate(begin, size()); }
346 
347  MemoryChunk_t& operator=(const MemoryChunk_t&) = delete;
349  MemoryChunk_t& operator=(MemoryChunk_t&&);
350 
352  size_type size() const { return end - begin; }
353 
355  size_type available() const { return end - free; }
356 
358  size_type used() const { return free - begin; }
359 
361  bool full() const { return !available(); }
362 
364  pointer get() { return (free != end) ? free++ : nullptr; }
365 
367  pointer get(size_t n)
368  {
369  pointer ptr = free;
370  if ((free += n) <= end) return ptr;
371  free = ptr;
372  return nullptr;
373  }
374 
375  private:
377  MemoryChunk_t() : allocator(nullptr) {}
378 
379  }; // class MemoryChunk_t
380 
381  typedef std::vector<MemoryChunk_t> MemoryPool_t;
382 
383  size_type ChunkSize;
384  MemoryPool_t MemoryPool;
385 
387  static size_type DefaultChunkSize;
388 
390  void Preallocate(size_type n);
391 
392  }; // class BulkAllocatorBase<>
393 
394  template <typename T>
396  {
397  std::swap(allocator, from.allocator);
398  std::swap(begin, from.begin);
399  std::swap(end, from.end);
400  std::swap(free, from.free);
401  } // BulkAllocatorBase<T>::MemoryChunk_t::MemoryChunk_t(MemoryChunk_t&&)
402 
403  template <typename T>
405  MemoryChunk_t&& from)
406  {
407  std::swap(allocator, from.allocator);
408  std::swap(begin, from.begin);
409  std::swap(end, from.end);
410  std::swap(free, from.free);
411  return *this;
412  } // BulkAllocatorBase<T>::MemoryChunk_t::operator= (MemoryChunk_t&&)
413 
414  template <typename T>
416 
417  template <typename T>
419  bool bPreallocate /* = false */)
420  : ChunkSize(NewChunkSize), MemoryPool()
421  {
422  Preallocate(bPreallocate ? ChunkSize : 0);
423  if (bDebug) {
424  std::cout << "BulkAllocatorBase[" << ((void*)this) << "] created for type "
425  << demangle<value_type>() << " with chunk size " << GetChunkSize() << " x"
426  << sizeof(value_type) << " byte => " << (GetChunkSize() * sizeof(value_type))
427  << " bytes/chunk" << std::endl;
428  } // if debug
429  } // BulkAllocatorBase<T>::BulkAllocatorBase()
430 
431  template <typename T>
433  {
434  if (bDebug) {
435  std::cout << "BulkAllocatorBase[" << ((void*)this) << "] freeing " << NChunks()
436  << " memory chunks with " << AllocatedCount() << " elements" << std::endl;
437  } // if debug
438  MemoryPool.clear();
439  } // BulkAllocatorBase<T>::Free()
440 
441  template <typename T>
443  {
445  if (hasUsers()) return true;
446  Free();
447  return false;
448  } // BulkAllocatorBase<T>::RemoveUser()
449 
450  template <typename T>
451  void BulkAllocatorBase<T>::AddUser(size_type NewChunkSize, bool bPreallocate /* = false */)
452  {
453  AddUser();
454  SetChunkSize(NewChunkSize);
455  Preallocate(bPreallocate ? ChunkSize : 0);
456  } // BulkAllocatorBase<T>::AddUser(size_type, bool )
457 
458  template <typename T>
460  {
461  if (MemoryPool.empty() || (MemoryPool.front().available() < n))
462  MemoryPool.emplace_back(allocator, n);
463  } // BulkAllocatorBase<T>::RemoveUser()
464 
465  template <typename T>
467  {
468  size_type n = 0;
469  for (const auto& chunk : MemoryPool)
470  n += chunk.size();
471  return n;
472  } // AllocatedCount()
473 
474  template <typename T>
476  {
477  size_type n = 0;
478  for (const auto& chunk : MemoryPool)
479  n += chunk.used();
480  return n;
481  } // BulkAllocatorBase<T>::UsedCount()
482 
483  template <typename T>
484  std::array<typename BulkAllocatorBase<T>::size_type, 2> BulkAllocatorBase<T>::GetCounts()
485  const
486  {
487  // BUG the double brace syntax is required to work around clang bug 21629
488  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
489  std::array<BulkAllocatorBase<T>::size_type, 2> stats = {{0U, 0U}};
490  for (const auto& chunk : MemoryPool) {
491  stats[0] += chunk.used();
492  stats[1] += chunk.available();
493  } // for
494  return stats;
495  } // BulkAllocatorBase<T>::GetCounts()
496 
497  template <typename T>
498  void BulkAllocatorBase<T>::SetChunkSize(size_type NewChunkSize, bool force /* = false */)
499  {
500  if ((GetChunkSize() == NewChunkSize) && !force) return;
501  if (bDebug) {
502  std::cout << "BulkAllocatorBase[" << ((void*)this) << "]"
503  << " changing chunk size: " << GetChunkSize() << " => " << NewChunkSize << ": x"
504  << sizeof(value_type) << " byte => " << (NewChunkSize * sizeof(value_type))
505  << " bytes/chunk" << std::endl;
506  }
507  ChunkSize = NewChunkSize;
508  } // BulkAllocatorBase<T>::SetChunkSize()
509 
510  template <typename T>
512  {
513  if (n == 0) return nullptr;
514  // get the free pointer from the latest chunk (#0)
515  pointer ptr = MemoryPool.front().get(n);
516  if (ptr) return ptr;
517  // no free element left in that chunk:
518  // - create a new one in the first position of the pool (move the rest)
519  // - return the pointer from the new pool
520  // (if it fails, it fails... but how could that happen?)
521  if (bDebug) {
522  std::array<size_type, 2> stats = GetCounts();
523  std::cout << "BulkAllocatorBase[" << ((void*)this) << "] allocating "
524  << std::max(ChunkSize, n) << " more elements (on top of the current "
525  << (stats[0] + stats[1]) << " elements, " << stats[1] << " unused)"
526  << std::endl;
527  } // if debug
528  return MemoryPool.emplace(MemoryPool.begin(), allocator, std::max(ChunkSize, n))->get(n);
529  } // BulkAllocatorBase<T>::Get()
530 
531  } // namespace bulk_allocator
532  } // namespace details
533 
534  template <typename T>
536 
537  template <typename T>
538  void BulkAllocator<T>::CreateGlobalAllocator(size_type ChunkSize, bool bPreallocate /* = false */)
539  {
540  GlobalAllocator.AddUser(ChunkSize, bPreallocate);
541  } // BulkAllocator<T>::CreateGlobalAllocator()
542 
543  template <typename T>
545  const void* /* hint = 0 */)
546  {
547  return GlobalAllocator.Get(n);
548  }
549 
550  template <typename T>
552  {
553  return GlobalAllocator.Release(p);
554  } // BulkAllocator<T>::deallocate()
555 } // namespace lar
556 
557 #endif // BULKALLOCATOR_H
A class managing a memory pool.
Definition: BulkAllocator.h:29
BaseAllocator_t::pointer pointer
Definition: BulkAllocator.h:99
MemoryChunk_t & operator=(const MemoryChunk_t &)=delete
Can&#39;t assign.
void Release(pointer)
Releases memory pointed by the specified pointer (but it does not).
Internal memory chunk; like a std::vector, but does not construct.
BaseAllocator_t::difference_type difference_type
Definition: BulkAllocator.h:95
BulkAllocatorBase(size_type NewChunkSize=DefaultChunkSize, bool bPreallocate=false)
Constructor; preallocates memory if explicitly requested.
bool RemoveUser()
Removed a user to the users count; returns false if no user yet.
void deallocate(pointer p, size_type n)
Frees n elements at p.
void CreateGlobalAllocator(size_type ChunkSize, bool bPreallocate=false)
Makes sure we have a valid "global allocator".
Counter_t Count() const
Returns the number of registered users.
pointer Get(size_type n)
Returns a pointer to memory for n new values of type T.
std::array< size_type, 2 > GetCounts() const
Returns an array equivalent to { UsedCount(), FreeCount() }.
void AddUser()
Adds a user to the users count.
Allocator_t::difference_type difference_type
void Free()
Releases the pool of memory; all pointer to it become invalid.
void message(RunManager *runmanager)
Definition: ts_scorers.cc:74
memory_error(const char *message)
Definition: BulkAllocator.h:37
STL namespace.
static void Free()
Releases all the allocated memory: dangerous!
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
static SharedAllocator_t GlobalAllocator
The allocator shared by all instances of this object.
BaseAllocator_t::reference reference
~BulkAllocator()
Destructor; memory is freed only if no allocators are left around.
bool full() const
Returns whether the chunk is full.
Exception thrown when BulkAllocator-specific allocation errors happen.
Definition: BulkAllocator.h:34
size_type ChunkSize
size of the chunks to add
BulkAllocator() noexcept
Default constructor: uses the default chunk size.
size_type AllocatedCount() const
Returns the total number of entries in the pool.
BaseAllocator_t::size_type size_type
Definition: BulkAllocator.h:94
BulkAllocator< U > other
BulkAllocator(const BulkAllocator< U > &a) noexcept
General copy constructor; currently, it does not preallocate.
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
Allocator_t * allocator
reference to the allocator to be used
A simple reference counter, keep track of a number of users.
BaseAllocator_t::const_reference const_reference
pointer allocate(size_type n, const void *=0)
Allocates memory for n elements.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:295
BulkAllocator(size_type ChunkSize, bool bPreallocate=false) noexcept
Constructor: sets chunk size and optionally allocates the first chunk.
virtual const char * what() const override
Definition: BulkAllocator.h:39
~BulkAllocatorBase()
Destructor: releases all the memory pool (.
size_type size() const
Returns the number of elements in this pool.
details::bulk_allocator::BulkAllocatorBase< T > SharedAllocator_t
shared allocator type
Allocator_t allocator
the actual allocator we use
Aggressive allocator reserving a lot of memory in advance.
Definition: BulkAllocator.h:89
std::string demangle()
Demangles the name of a type.
size_type NChunks() const
Returns the number of memory pool chunks allocated.
static size_type DefaultChunkSize
Default chunk size (default: 10000)
size_type available() const
Returns the number of free elements in this pool.
size_type UsedCount() const
Returns the total number of used entries in the pool.
MemoryChunk_t()
< Default constructor (does nothing)
size_type GetChunkSize() const
Returns the current chunk size.
bool hasUsers() const
Returns whether there are currently users.
unsigned int Counter_t
type of user counter
LArSoft-specific namespace.
static void SetChunkSize(size_type ChunkSize)
Sets chunk size of global allocator; only affects future allocations!
BaseAllocator_t::value_type value_type
Definition: BulkAllocator.h:97
BaseAllocator_t::const_pointer const_pointer
Char_t n[5]
void SetChunkSize(size_type NewChunkSize, bool force=false)
Sets the chunk size for the future allocations.
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
std::allocator< T > BaseAllocator_t
Definition: BulkAllocator.h:91
size_type used() const
Returns the number of used elements in this pool.
static size_type GetChunkSize()
Returns the chunk size of the underlying global allocator.
bool RemoveUser()
Removed a user to the users count; if no user is left, free the pool.
MemoryPool_t MemoryPool
list of all memory chunks; first is free
void AddUser()
Add a new pool user with the current parameters.