RAJA
RAJA provides a collection of platform portability abstractions for C++ HPC applications.
basic_mempool.hpp
Go to the documentation of this file.
1 
11 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
12 // Copyright (c) Lawrence Livermore National Security, LLC and other
13 // RAJA Project Developers. See top-level LICENSE and COPYRIGHT
14 // files for dates and other details. No copyright assignment is required
15 // to contribute to RAJA.
16 //
17 // SPDX-License-Identifier: (BSD-3-Clause)
18 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
19 
20 #ifndef RAJA_BASIC_MEMPOOL_HPP
21 #define RAJA_BASIC_MEMPOOL_HPP
22 
23 #include <cassert>
24 #include <cstddef>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <list>
28 #include <map>
29 #include <mutex>
30 
31 #include "RAJA/util/align.hpp"
32 
33 namespace RAJA
34 {
35 
36 namespace basic_mempool
37 {
38 
39 namespace detail
40 {
41 
42 
57 {
58 public:
59  using free_type = std::map<void*, void*>;
60  using free_value_type = typename free_type::value_type;
61  using used_type = std::map<void*, void*>;
62  using used_value_type = typename used_type::value_type;
63 
64  MemoryArena(void* ptr, size_t size)
65  : m_allocation {ptr, static_cast<char*>(ptr) + size},
66  m_free_space(),
67  m_used_space()
68  {
69  m_free_space[ptr] = static_cast<char*>(ptr) + size;
70  if (m_allocation.begin == nullptr)
71  {
72  fprintf(stderr, "Attempt to create MemoryArena with no memory");
73  std::abort();
74  }
75  }
76 
77  MemoryArena(MemoryArena const&) = delete;
78  MemoryArena& operator=(MemoryArena const&) = delete;
79 
80  MemoryArena(MemoryArena&&) = default;
82 
83  size_t capacity()
84  {
85  return static_cast<char*>(m_allocation.end) -
86  static_cast<char*>(m_allocation.begin);
87  }
88 
89  bool unused() { return m_used_space.empty(); }
90 
91  void* get_allocation() { return m_allocation.begin; }
92 
93  void* get(size_t nbytes, size_t alignment)
94  {
95  void* ptr_out = nullptr;
96  if (capacity() >= nbytes)
97  {
98  free_type::iterator end = m_free_space.end();
99  for (free_type::iterator iter = m_free_space.begin(); iter != end; ++iter)
100  {
101 
102  void* adj_ptr = iter->first;
103  size_t cap =
104  static_cast<char*>(iter->second) - static_cast<char*>(adj_ptr);
105 
106  if (::RAJA::align(alignment, nbytes, adj_ptr, cap))
107  {
108 
109  ptr_out = adj_ptr;
110 
111  remove_free_chunk(iter, adj_ptr,
112  static_cast<char*>(adj_ptr) + nbytes);
113 
114  add_used_chunk(adj_ptr, static_cast<char*>(adj_ptr) + nbytes);
115 
116  break;
117  }
118  }
119  }
120  return ptr_out;
121  }
122 
123  bool give(void* ptr)
124  {
125  if (m_allocation.begin <= ptr && ptr < m_allocation.end)
126  {
127 
128  used_type::iterator found = m_used_space.find(ptr);
129 
130  if (found != m_used_space.end())
131  {
132 
133  add_free_chunk(found->first, found->second);
134 
135  m_used_space.erase(found);
136  }
137  else
138  {
139  fprintf(stderr, "Invalid free %p", ptr);
140  std::abort();
141  }
142 
143  return true;
144  }
145  else
146  {
147  return false;
148  }
149  }
150 
151 private:
152  struct memory_chunk
153  {
154  void* begin;
155  void* end;
156  };
157 
158  void add_free_chunk(void* begin, void* end)
159  {
160  // integrates a chunk of memory into free_space
161  free_type::iterator invl = m_free_space.end();
162  free_type::iterator next = m_free_space.lower_bound(begin);
163 
164  // check if prev exists
165  if (next != m_free_space.begin())
166  {
167  // check if prev can cover [begin, end)
168  free_type::iterator prev = next;
169  --prev;
170  if (prev->second == begin)
171  {
172  // extend prev to cover [begin, end)
173  prev->second = end;
174 
175  // check if prev can cover next too
176  if (next != invl)
177  {
178  assert(next->first != begin);
179 
180  if (next->first == end)
181  {
182  // extend prev to cover next too
183  prev->second = next->second;
184 
185  // remove redundant next
186  m_free_space.erase(next);
187  }
188  }
189  return;
190  }
191  }
192 
193  if (next != invl)
194  {
195  assert(next->first != begin);
196 
197  if (next->first == end)
198  {
199  // extend next to cover [begin, end)
200  m_free_space.insert(next, free_value_type {begin, next->second});
201  m_free_space.erase(next);
202 
203  return;
204  }
205  }
206 
207  // no free space adjacent to this chunk, add seperate free chunk [begin,
208  // end)
209  m_free_space.insert(next, free_value_type {begin, end});
210  }
211 
212  void remove_free_chunk(free_type::iterator iter, void* begin, void* end)
213  {
214 
215  void* ptr = iter->first;
216  void* ptr_end = iter->second;
217 
218  // fixup m_free_space, shrinking and adding chunks as needed
219  if (ptr != begin)
220  {
221 
222  // shrink end of current free region to [ptr, begin)
223  iter->second = begin;
224 
225  if (end != ptr_end)
226  {
227 
228  // insert free region [end, ptr_end) after current free region
229  free_type::iterator next = iter;
230  ++next;
231  m_free_space.insert(next, free_value_type {end, ptr_end});
232  }
233  }
234  else if (end != ptr_end)
235  {
236 
237  // shrink beginning of current free region to [end, ptr_end)
238  free_type::iterator next = iter;
239  ++next;
240  m_free_space.insert(next, free_value_type {end, ptr_end});
241  m_free_space.erase(iter);
242  }
243  else
244  {
245 
246  // can not reuse current region, erase
247  m_free_space.erase(iter);
248  }
249  }
250 
251  void add_used_chunk(void* begin, void* end)
252  {
253  // simply inserts a chunk of memory into used_space
254  m_used_space.insert(used_value_type {begin, end});
255  }
256 
257  memory_chunk m_allocation;
258  free_type m_free_space;
259  used_type m_used_space;
260 };
261 
262 } /* end namespace detail */
263 
304 template<typename allocator_t>
305 class MemPool
306 {
307 public:
308  using allocator_type = allocator_t;
309 
311  {
312  static MemPool<allocator_t> pool {};
313  return pool;
314  }
315 
316  static const size_t default_default_arena_size = 32ull * 1024ull * 1024ull;
317 
319  : m_arenas(),
320  m_default_arena_size(default_default_arena_size),
321  m_alloc()
322  {}
323 
325  {
326  // With static objects like MemPool, cudaErrorCudartUnloading is a possible
327  // error with cudaFree
328  // So no more cuda calls here
329  }
330 
332  void free_chunks()
333  {
334  std::lock_guard<std::mutex> lock(m_mutex);
335 
336  while (!m_arenas.empty())
337  {
338  void* allocation_ptr = m_arenas.front().get_allocation();
339  m_alloc.free(allocation_ptr);
340  m_arenas.pop_front();
341  }
342  }
343 
344  size_t arena_size()
345  {
346  std::lock_guard<std::mutex> lock(m_mutex);
347 
348  return m_default_arena_size;
349  }
350 
351  size_t arena_size(size_t new_size)
352  {
353  std::lock_guard<std::mutex> lock(m_mutex);
354 
355  size_t prev_size = m_default_arena_size;
356  m_default_arena_size = new_size;
357  return prev_size;
358  }
359 
360  template<typename T>
361  T* malloc(size_t nTs, size_t alignment = alignof(T))
362  {
363  std::lock_guard<std::mutex> lock(m_mutex);
364 
365  const size_t size = nTs * sizeof(T);
366  void* ptr = nullptr;
367  arena_container_type::iterator end = m_arenas.end();
368  for (arena_container_type::iterator iter = m_arenas.begin(); iter != end;
369  ++iter)
370  {
371  ptr = iter->get(size, alignment);
372  if (ptr != nullptr)
373  {
374  break;
375  }
376  }
377 
378  if (ptr == nullptr)
379  {
380  const size_t alloc_size =
381  std::max(size + alignment, m_default_arena_size);
382  void* arena_ptr = m_alloc.malloc(alloc_size);
383  if (arena_ptr != nullptr)
384  {
385  m_arenas.emplace_front(arena_ptr, alloc_size);
386  ptr = m_arenas.front().get(size, alignment);
387  }
388  }
389 
390  return static_cast<T*>(ptr);
391  }
392 
393  void free(const void* cptr)
394  {
395  std::lock_guard<std::mutex> lock(m_mutex);
396 
397  void* ptr = const_cast<void*>(cptr);
398  arena_container_type::iterator end = m_arenas.end();
399  for (arena_container_type::iterator iter = m_arenas.begin(); iter != end;
400  ++iter)
401  {
402  if (iter->give(ptr))
403  {
404  ptr = nullptr;
405  break;
406  }
407  }
408  if (ptr != nullptr)
409  {
410  fprintf(stderr, "Unknown pointer %p", ptr);
411  }
412  }
413 
414 private:
415  using arena_container_type = std::list<detail::MemoryArena>;
416 
417  std::mutex m_mutex;
418 
419  arena_container_type m_arenas;
420  size_t m_default_arena_size;
421  allocator_t m_alloc;
422 };
423 
426 {
427 
428  // returns a valid pointer on success, nullptr on failure
429  void* malloc(size_t nbytes) { return std::malloc(nbytes); }
430 
431  // returns true on success, false on failure
432  bool free(void* ptr)
433  {
434  std::free(ptr);
435  return true;
436  }
437 };
438 
439 } /* end namespace basic_mempool */
440 
441 } /* end namespace RAJA */
442 
443 
444 #endif /* RAJA_BASIC_MEMPOOL_HPP */
RAJA header file containing an implementation of std align.
MemPool pre-allocates a large chunk of memory and provides generic malloc/free for the user to alloca...
Definition: basic_mempool.hpp:306
void free(const void *cptr)
Definition: basic_mempool.hpp:393
T * malloc(size_t nTs, size_t alignment=alignof(T))
Definition: basic_mempool.hpp:361
static MemPool< allocator_t > & getInstance()
Definition: basic_mempool.hpp:310
static const size_t default_default_arena_size
Definition: basic_mempool.hpp:316
size_t arena_size(size_t new_size)
Definition: basic_mempool.hpp:351
void free_chunks()
Free all backing allocations, even if they are currently in use.
Definition: basic_mempool.hpp:332
size_t arena_size()
Definition: basic_mempool.hpp:344
~MemPool()
Definition: basic_mempool.hpp:324
allocator_t allocator_type
Definition: basic_mempool.hpp:308
MemPool()
Definition: basic_mempool.hpp:318
MemoryArena is a map based subclass for class MemPool provides book-keeping to divy a large chunk of ...
Definition: basic_mempool.hpp:57
MemoryArena & operator=(MemoryArena const &)=delete
std::map< void *, void * > free_type
Definition: basic_mempool.hpp:59
MemoryArena(MemoryArena const &)=delete
typename used_type::value_type used_value_type
Definition: basic_mempool.hpp:62
typename free_type::value_type free_value_type
Definition: basic_mempool.hpp:60
void * get(size_t nbytes, size_t alignment)
Definition: basic_mempool.hpp:93
bool give(void *ptr)
Definition: basic_mempool.hpp:123
MemoryArena & operator=(MemoryArena &&)=default
MemoryArena(void *ptr, size_t size)
Definition: basic_mempool.hpp:64
std::map< void *, void * > used_type
Definition: basic_mempool.hpp:61
bool unused()
Definition: basic_mempool.hpp:89
void * get_allocation()
Definition: basic_mempool.hpp:91
size_t capacity()
Definition: basic_mempool.hpp:83
value_type::device_call &[i_loop] iter
Definition: WorkRunner.hpp:216
Definition: AlignedRangeIndexSetBuilders.cpp:35
RAJA_HOST_DEVICE RAJA_INLINE Iter next(Iter it)
returns iterator to next item
Definition: algorithm.hpp:90
RAJA_INLINE void * align(size_t alignment, size_t size, void *&ptr, size_t &space)
Definition: align.hpp:33
RAJA_HOST_DEVICE RAJA_INLINE Iter prev(Iter it)
returns iterator to next item
Definition: algorithm.hpp:100
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result max(Args... args)
Definition: foldl.hpp:155
example allocator for basic_mempool using malloc/free
Definition: basic_mempool.hpp:426
void * malloc(size_t nbytes)
Definition: basic_mempool.hpp:429
bool free(void *ptr)
Definition: basic_mempool.hpp:432