LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
BulkAllocator.h
Go to the documentation of this file.
1 
9 //--- BEGIN issue #19494 -------------------------------------------------------
11 // We are leaving it here because, being a header, it will not bother unless
12 // explicitly invoked. Note that there is a unit test for it too.
13 #error ("BulkAllocator.h is currently broken; see issue #19494.")
14 //--- END issue #19494 ---------------------------------------------------------
15 
16 
17 #ifndef BULKALLOCATOR_H
18 #define BULKALLOCATOR_H
19 
20 // interface include
21 #include <memory> // std::allocator<>, std::unique_ptr<>
22 #include <stdexcept> // std::logic_error
23 #include <cstdlib> // std::free
24 
25 namespace lar {
27  namespace details {
29  namespace bulk_allocator {
30  template <typename T>
32  } // namespace bulk_allocator
33  } // namespace details
34 
35 
37  class memory_error: public std::bad_alloc {
38  public:
39  memory_error(): std::bad_alloc() {}
40  memory_error(const char* message): std::bad_alloc(), msg(message) {}
41 
42  virtual const char* what() const throw() override { return msg; }
43 
44  private:
45  const char* msg = nullptr;
46  }; // class memory_error
47 
91  template <typename T>
92  class BulkAllocator: public std::allocator<T> {
93  public:
94  using BaseAllocator_t = std::allocator<T>;
95 
96  // import types from the STL allocator
97  typedef typename BaseAllocator_t::size_type size_type;
98  typedef typename BaseAllocator_t::difference_type difference_type;
99 
100  typedef typename BaseAllocator_t::value_type value_type;
101 
102  typedef typename BaseAllocator_t::pointer pointer;
103  typedef typename BaseAllocator_t::const_pointer const_pointer;
104 
105  typedef typename BaseAllocator_t::reference reference;
106  typedef typename BaseAllocator_t::const_reference const_reference;
107 
108  template<typename U>
109  struct rebind {
111  };
112 
114  BulkAllocator() noexcept: BulkAllocator(GetChunkSize(), false) {}
115 
117  BulkAllocator(size_type ChunkSize, bool bPreallocate = false) noexcept
118  { CreateGlobalAllocator(ChunkSize, bPreallocate); }
119 
121  BulkAllocator(const BulkAllocator &a) noexcept = default;
122 
124  BulkAllocator(BulkAllocator &&a) noexcept = default;
125 
127  template <class U>
128  BulkAllocator(const BulkAllocator<U> &a) noexcept:
129  BaseAllocator_t(a) { SetChunkSize(a.GetChunkSize()); }
130 
132  BulkAllocator& operator = (const BulkAllocator &a) = default;
133 
135  BulkAllocator& operator = (BulkAllocator &&a) = default;
136 
138  ~BulkAllocator() { GlobalAllocator.RemoveUser(); }
139 
141  pointer allocate(size_type n, const void* /* hint */ = 0);
142 
144  void deallocate(pointer p, size_type n);
145 
147  static void Free() { GlobalAllocator.Free(); }
148 
150  static size_type GetChunkSize() { return GlobalAllocator.GetChunkSize(); }
151 
153  static void SetChunkSize(size_type ChunkSize)
154  { GlobalAllocator.SetChunkSize(ChunkSize); }
155 
156  private:
159 
161  void CreateGlobalAllocator(size_type ChunkSize, bool bPreallocate = false);
162 
165 
166 
167  }; // class BulkAllocator<>
168 
169 } // namespace lar
170 
171 
172 //------------------------------------------------------------------------------
173 #include <algorithm> // std::max()
174 #include <vector>
175 #include <iostream>
176 #include <array>
177 #include <typeinfo>
178 #include <string>
179 #ifdef __GNUG__
180 # include <cxxabi.h>
181 #endif // __GNUG__
182 
183 namespace lar {
184  namespace details {
186 
204  template <typename T>
205  std::string demangle() {
206  std::string name = typeid(T).name();
207  #ifdef __GNUG__
208  int status; // some arbitrary value to eliminate the compiler warning
209  std::unique_ptr<char, void(*)(void*)> res
210  { abi::__cxa_demangle(name.c_str(), NULL, NULL, &status), std::free };
211  return (status==0) ? res.get() : name ;
212  #else // !__GNUG__
213  return name;
214  #endif // ?__GNUG__
215  } // demangle()
216 
217  template <typename T>
218  inline std::string demangle(const T&) { return demangle<T>(); }
220 
221  namespace bulk_allocator {
222  constexpr bool bDebug = false;
223 
226  public:
227  typedef unsigned int Counter_t;
228 
230  bool hasUsers() const { return counter > 0; }
231 
233  Counter_t Count() const { return counter; }
234 
236  void AddUser() { ++counter; }
237 
239  bool RemoveUser()
240  { if (!counter) return false; --counter; return true; }
241 
242  private:
243  Counter_t counter = 0;
244  }; // class ReferenceCounter
245 
246 
259  template <typename T>
260  class BulkAllocatorBase: public ReferenceCounter {
261  public:
262  typedef size_t size_type;
263  typedef T value_type;
264  typedef T* pointer;
265 
268  size_type NewChunkSize = DefaultChunkSize, bool bPreallocate = false
269  );
270 
272  ~BulkAllocatorBase() { Free(); }
273 
275  void Free();
276 
278  pointer Get(size_type n);
279 
281  void Release(pointer) {}
282 
285 
287  void AddUser(size_type NewChunkSize, bool bPreallocate = false);
288 
290  bool RemoveUser();
291 
293  size_type AllocatedCount() const;
294 
296  size_type UsedCount() const;
297 
299  size_type FreeCount() const;
300 
302  size_type NChunks() const { return MemoryPool.size(); }
303 
305  std::array<size_type, 2> GetCounts() const;
306 
308  void SetChunkSize(size_type NewChunkSize, bool force = false);
309 
311  size_type GetChunkSize() const { return ChunkSize; }
312 
315  void Preallocate() { Preallocate(ChunkSize); }
316 
317  private:
318  typedef std::allocator<T> Allocator_t;
319  typedef typename Allocator_t::difference_type difference_type;
320 
321  Allocator_t allocator;
322 
325  public:
326  Allocator_t* allocator;
327 
328  pointer begin = nullptr;
329  pointer end = nullptr;
330  pointer free = nullptr;
331 
333  MemoryChunk_t(Allocator_t& alloc, size_type n): allocator(&alloc)
334  {
335  begin = n? allocator->allocate(n): nullptr;
336  end = begin + n;
337  free = begin;
338  } // MemoryChunk_t()
339  MemoryChunk_t(const MemoryChunk_t&) = delete;
341 
342  ~MemoryChunk_t() { allocator->deallocate(begin, size()); }
343 
344  MemoryChunk_t& operator=(const MemoryChunk_t&) = delete;
346  MemoryChunk_t& operator=(MemoryChunk_t&&);
347 
349  size_type size() const { return end - begin; }
350 
352  size_type available() const { return end - free; }
353 
355  size_type used() const { return free - begin; }
356 
358  bool full() const { return !available(); }
359 
361  pointer get() { return (free != end)? free++: nullptr; }
362 
364  pointer get(size_t n)
365  {
366  pointer ptr = free;
367  if ((free += n) <= end) return ptr;
368  free = ptr;
369  return nullptr;
370  }
371 
372  private:
374  MemoryChunk_t(): allocator(nullptr) {}
375 
376  }; // class MemoryChunk_t
377 
378  typedef std::vector<MemoryChunk_t> MemoryPool_t;
379 
380  size_type ChunkSize;
381  MemoryPool_t MemoryPool;
382 
384  static size_type DefaultChunkSize;
385 
387  void Preallocate(size_type n);
388 
389  }; // class BulkAllocatorBase<>
390 
391 
392  template <typename T>
394  std::swap(allocator, from.allocator);
395  std::swap(begin, from.begin);
396  std::swap(end, from.end);
397  std::swap(free, from.free);
398  } // BulkAllocatorBase<T>::MemoryChunk_t::MemoryChunk_t(MemoryChunk_t&&)
399 
400  template <typename T>
403  {
404  std::swap(allocator, from.allocator);
405  std::swap(begin, from.begin);
406  std::swap(end, from.end);
407  std::swap(free, from.free);
408  return *this;
409  } // BulkAllocatorBase<T>::MemoryChunk_t::operator= (MemoryChunk_t&&)
410 
411 
412  template <typename T>
415 
416  template <typename T>
418  (size_type NewChunkSize, bool bPreallocate /* = false */):
419  ChunkSize(NewChunkSize), MemoryPool()
420  {
421  Preallocate(bPreallocate? ChunkSize: 0);
422  if (bDebug) {
423  std::cout << "BulkAllocatorBase[" << ((void*) this)
424  << "] created for type " << demangle<value_type>()
425  << " with chunk size " << GetChunkSize()
426  << " x" << sizeof(value_type) << " byte => "
427  << (GetChunkSize()*sizeof(value_type)) << " bytes/chunk"
428  << std::endl;
429  } // if debug
430  } // BulkAllocatorBase<T>::BulkAllocatorBase()
431 
432 
433  template <typename T>
435  if (bDebug) {
436  std::cout << "BulkAllocatorBase[" << ((void*) this) << "] freeing "
437  << NChunks() << " memory chunks with " << AllocatedCount()
438  << " elements" << std::endl;
439  } // if debug
440  MemoryPool.clear();
441  } // BulkAllocatorBase<T>::Free()
442 
443 
444  template <typename T>
447  if (hasUsers()) return true;
448  Free();
449  return false;
450  } // BulkAllocatorBase<T>::RemoveUser()
451 
452 
453  template <typename T>
455  (size_type NewChunkSize, bool bPreallocate /* = false */)
456  {
457  AddUser();
458  SetChunkSize(NewChunkSize);
459  Preallocate(bPreallocate? ChunkSize: 0);
460  } // BulkAllocatorBase<T>::AddUser(size_type, bool )
461 
462 
463  template <typename T>
465  if (MemoryPool.empty() || (MemoryPool.front().available() < n))
466  MemoryPool.emplace_back(allocator, n);
467  } // BulkAllocatorBase<T>::RemoveUser()
468 
469 
470  template <typename T>
473  {
474  size_type n = 0;
475  for (const auto& chunk: MemoryPool) n += chunk.size();
476  return n;
477  } // AllocatedCount()
478 
479 
480  template <typename T>
483  {
484  size_type n = 0;
485  for (const auto& chunk: MemoryPool) n += chunk.used();
486  return n;
487  } // BulkAllocatorBase<T>::UsedCount()
488 
489 
490  template <typename T>
491  std::array<typename BulkAllocatorBase<T>::size_type, 2>
493  {
494  // BUG the double brace syntax is required to work around clang bug 21629
495  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
496  std::array<BulkAllocatorBase<T>::size_type, 2> stats = {{ 0U, 0U }};
497  for (const auto& chunk: MemoryPool) {
498  stats[0] += chunk.used();
499  stats[1] += chunk.available();
500  } // for
501  return stats;
502  } // BulkAllocatorBase<T>::GetCounts()
503 
504 
505  template <typename T>
507  (size_type NewChunkSize, bool force /* = false */)
508  {
509  if ((GetChunkSize() == NewChunkSize) && !force) return;
510  if (bDebug) {
511  std::cout << "BulkAllocatorBase[" << ((void*) this) << "]"
512  << " changing chunk size: " << GetChunkSize() << " => "
513  << NewChunkSize << ": x" << sizeof(value_type) << " byte => "
514  << (NewChunkSize*sizeof(value_type)) << " bytes/chunk"
515  << std::endl;
516  }
517  ChunkSize = NewChunkSize;
518  } // BulkAllocatorBase<T>::SetChunkSize()
519 
520 
521  template <typename T>
524  {
525  if (n == 0) return nullptr;
526  // get the free pointer from the latest chunk (#0)
527  pointer ptr = MemoryPool.front().get(n);
528  if (ptr) return ptr;
529  // no free element left in that chunk:
530  // - create a new one in the first position of the pool (move the rest)
531  // - return the pointer from the new pool
532  // (if it fails, it fails... but how could that happen?)
533  if (bDebug) {
534  std::array<size_type, 2> stats = GetCounts();
535  std::cout << "BulkAllocatorBase[" << ((void*) this)
536  << "] allocating " << std::max(ChunkSize, n)
537  << " more elements (on top of the current " << (stats[0] + stats[1])
538  << " elements, " << stats[1] << " unused)" << std::endl;
539  } // if debug
540  return MemoryPool.emplace
541  (MemoryPool.begin(), allocator, std::max(ChunkSize, n))->get(n);
542  } // BulkAllocatorBase<T>::Get()
543 
544 
545  } // namespace bulk_allocator
546  } // namespace details
547 
548 
549  template <typename T>
552 
553  template <typename T>
555  (size_type ChunkSize, bool bPreallocate /* = false */)
556  {
557  GlobalAllocator.AddUser(ChunkSize, bPreallocate);
558  } // BulkAllocator<T>::CreateGlobalAllocator()
559 
560  template <typename T>
562  (size_type n, const void * /* hint = 0 */)
563  { return GlobalAllocator.Get(n); }
564 
565  template <typename T>
567  return GlobalAllocator.Release(p);
568  } // BulkAllocator<T>::deallocate()
569 } // namespace lar
570 
571 
572 #endif // BULKALLOCATOR_H
573 
A class managing a memory pool.
Definition: BulkAllocator.h:31
BaseAllocator_t::pointer pointer
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:98
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.
memory_error(const char *message)
Definition: BulkAllocator.h:40
STL namespace.
static void Free()
Releases all the allocated memory: dangerous!
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:37
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:97
BulkAllocator< U > other
cout<< "Opened file "<< fin<< " ixs= "<< ixs<< endl;if(ixs==0) hhh=(TH1F *) fff-> Get("h1")
Definition: AddMC.C:8
BulkAllocator(const BulkAllocator< U > &a) noexcept
General copy constructor; currently, it does not preallocate.
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.
Int_t max
Definition: plot.C:27
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:42
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
~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:92
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
BaseAllocator_t::const_pointer const_pointer
Char_t n[5]
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
void SetChunkSize(size_type NewChunkSize, bool force=false)
Sets the chunk size for the future allocations.
std::allocator< T > BaseAllocator_t
Definition: BulkAllocator.h:94
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.