Nektar++
NekMemoryManager.hpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: NekMemoryManager.hpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description:
32 //
33 // A memory manager that allocates memory from thread specific pools.
34 //
35 ////////////////////////////////////////////////////////////////////////////////
36 
37 #ifndef NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
38 #define NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
39 
40 #include <memory>
41 #include <type_traits>
42 
43 #include <boost/core/ignore_unused.hpp>
44 
48 
49 #include <vector>
50 
51 #ifdef max
52 #undef max
53 #endif
54 
55 namespace Nektar
56 {
57 
58 /// @brief General purpose memory allocation routines with the ability
59 /// to allocate from thread specific memory pools.
60 ///
61 /// If compiled with NEKTAR_MEMORY_POOL_ENABLED, the MemoryManager
62 /// allocates from thread specific memory pools for small objects.
63 /// Large objects are managed with the system supplied new/delete.
64 /// These memory pools provide faster allocation and deallocation
65 /// of small objects (particularly useful for shared pointers which
66 /// allocate many 4 byte objects).
67 ///
68 /// @warning All memory allocated from the memory manager must be returned
69 /// to the memory manager. Calling delete on memory allocated from the
70 /// manager will likely cause undefined behavior. A particularly subtle
71 /// violation of this rule occurs when giving memory allocated from the
72 /// manager to a shared pointer.
73 /// @code
74 /// std::shared_ptr<Obj> f(MemoryManager<Obj>::Allocate());
75 /// @endcode
76 /// Shared pointers call delete when they go out of scope, so this line of
77 /// code will cause problems. Instead, you should call the
78 /// AllocateSharedPtr method:
79 /// @code
80 /// std::shared_ptr<Obj> f = MemoryManager<Obj>::AllocateSharedPtr();
81 /// @endcode
82 template <typename DataType> class MemoryManager
83 {
84 public:
85  /// @brief Deallocate a pointer allocated by
86  /// MemoryManager::Allocate.
87  /// @note Results are undefined if called with a pointer to
88  /// something that was not allocated with the memory manager.
89  ///
90  /// Use this method to deallocate a pointer you have allocated from
91  /// the MemoryManager using the Allocate method.
92  ///
93  /// Example:
94  /// @code
95  /// CustObj* c = MemoryManager::Allocate<CustObj>();
96  /// MemoryManager::Deallocate(c);
97  /// @endcode
98  static void Deallocate(DataType *&data)
99  {
100 #ifdef NEKTAR_MEMORY_POOL_ENABLED
101  data->~DataType();
102  GetMemoryPool().Deallocate(data, sizeof(DataType));
103 #else
104 #ifdef NEKTAR_USE_ALIGNED_MEM
105  boost::alignment::aligned_free(data);
106 #else
107  delete data;
108 #endif
109 #endif
110 
111  data = NULL;
112  }
113 
114 #ifdef NEKTAR_MEMORY_POOL_ENABLED
115  /// @brief Allocates a single object from the memory pool.
116  /// @throws unknown If the object throws an exception during
117  /// construction, this method will catch it, release the memory
118  /// back to the pool, then rethrow it.
119  ///
120  /// The allocated object must be returned to the memory pool
121  /// via Deallocate.
122  template <typename... Args> static DataType *Allocate(const Args &...args)
123  {
124  DataType *result =
125  static_cast<DataType *>(GetMemoryPool().Allocate(sizeof(DataType)));
126 
127  if (result)
128  {
129  try
130  {
131  new (result) DataType(args...);
132  }
133  catch (...)
134  {
135  GetMemoryPool().Deallocate(result, sizeof(DataType));
136  throw;
137  }
138  }
139 
140  return result;
141  }
142 
143 #else // NEKTAR_MEMORY_POOL_ENABLED
144  /// @brief Allocates a single object from the memory pool.
145  /// @throws unknown Any exception thrown by DataType's default
146  /// constructor will propogate through this method.
147  ///
148  /// The allocated object must be returned to the memory pool
149  /// via Deallocate.
150  template <typename... Args> static DataType *Allocate(const Args &...args)
151  {
152 #ifdef NEKTAR_USE_ALIGNED_MEM
153  void *ptr = boost::alignment::aligned_alloc(
154  tinysimd::simd<NekDouble>::alignment, sizeof(DataType));
155  return new (ptr) DataType(args...);
156 #else
157  return new DataType(args...);
158 #endif
159  }
160 #endif // NEKTAR_MEMORY_POOL_ENABLED
161 
162  /// @brief Allocate a shared pointer from the memory pool.
163  ///
164  /// The shared pointer does not need to be returned to the memory
165  /// pool. When the reference count to this object reaches 0, the
166  /// shared pointer will automatically return the memory.
167  template <typename... Args>
168  static std::shared_ptr<DataType> AllocateSharedPtr(const Args &...args)
169  {
170  return AllocateSharedPtrD([](DataType *) {}, args...);
171  }
172 
173  template <typename DeallocatorType, typename... Args>
174  static std::shared_ptr<DataType> AllocateSharedPtrD(
175  const DeallocatorType &d, const Args &...args)
176  {
177  DataType *data = Allocate(args...);
178  return std::shared_ptr<DataType>(data, [=](DataType *ptr) {
179  d(ptr);
181  });
182  }
183 
184  /// \brief Allocates a chunk of raw, uninitialized memory, capable of
185  /// holding NumberOfElements objects.
186  ///
187  /// \param NumberOfElements The number of elements the array should be
188  /// capable of holding.
189  ///
190  /// This method is not meant to be called by client code. Use Array
191  /// instead. Any memory allocated from this method must be returned to the
192  /// memory pool via RawDeallocate. Failure to do so will result in memory
193  /// leaks and undefined behavior.
194  static DataType *RawAllocate(size_t NumberOfElements)
195  {
196 #ifdef NEKTAR_MEMORY_POOL_ENABLED
197  return static_cast<DataType *>(
198  GetMemoryPool().Allocate(NumberOfElements * sizeof(DataType)));
199 #else // NEKTAR_MEMORY_POOL_ENABLED
200 #ifdef NEKTAR_USE_ALIGNED_MEM
201  return static_cast<DataType *>(boost::alignment::aligned_alloc(
203  NumberOfElements * sizeof(DataType)));
204 #else
205  return static_cast<DataType *>(
206  ::operator new(NumberOfElements * sizeof(DataType)));
207 #endif
208 #endif // NEKTAR_MEMORY_POOL_ENABLED
209  }
210 
211  /// \brief Deallocates memory allocated from RawAllocate.
212  /// \param array A pointer to the memory returned from RawAllocate.
213  /// \param NumberOfElements The number of object held in the array.
214  ///
215  /// This method is not meant to be called by client code. Use Array
216  /// instead. Only memory allocated via RawAllocate should be returned to the
217  /// pool here.
218  static void RawDeallocate(DataType *array, size_t NumberOfElements)
219  {
220 #ifdef NEKTAR_MEMORY_POOL_ENABLED
221  GetMemoryPool().Deallocate(array, sizeof(DataType) * NumberOfElements);
222 #else // NEKTAR_MEMORY_POOL_ENABLED
223  boost::ignore_unused(NumberOfElements);
224 #ifdef NEKTAR_USE_ALIGNED_MEM
225  boost::alignment::aligned_free(array);
226 #else
227  ::operator delete(array);
228 #endif
229 #endif // NEKTAR_MEMORY_POOL_ENABLED
230  }
231 
232  /////////////////////////////////////////////////////////////////
233  ///\name Allocator Interface
234  /// The allocator interface allows a MemoryManager object to be used
235  /// in any object that allows an allocator parameter, such as STL
236  /// containers.
237  /////////////////////////////////////////////////////////////////
238  typedef DataType value_type;
239  typedef size_t size_type;
240  typedef ptrdiff_t difference_type;
241  typedef DataType *pointer;
242  typedef const DataType *const_pointer;
243  typedef DataType &reference;
244  typedef const DataType &const_reference;
245 
247  {
248  }
249  template <typename T> MemoryManager(const MemoryManager<T> &rhs)
250  {
251  boost::ignore_unused(rhs);
252  }
254  {
255  }
256 
258  {
259  return &r;
260  }
262  {
263  return &r;
264  }
265 
267  std::allocator<void>::const_pointer hint =
268  0) // typename MemoryManager<void>::pointer hint = 0)
269  {
270  boost::ignore_unused(hint);
271  return RawAllocate(n);
272  }
273 
275  {
276  return RawDeallocate(p, n);
277  }
278 
280  {
281  new (p) DataType(val);
282  }
283 
285  {
286  p->~DataType();
287  }
288 
290  {
291  return std::numeric_limits<size_type>::max() / sizeof(DataType);
292  }
293 
294  template <typename U> struct rebind
295  {
297  };
298 
299  /////////////////////////////////////////////////////////////////
300  ///@}
301  /////////////////////////////////////////////////////////////////
302 
303 private:
304 };
305 
306 template <typename DataType>
308  const MemoryManager<DataType> &rhs)
309 {
310  boost::ignore_unused(lhs, rhs);
311  return true;
312 }
313 
314 template <typename DataType>
316  const MemoryManager<DataType> &rhs)
317 {
318  return !(lhs == rhs);
319 }
320 
321 } // namespace Nektar
322 
323 #endif // NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
void Deallocate(void *p, size_t bytes)
Deallocate memory claimed by an earlier call to allocate.
void * Allocate(size_t bytes)
Allocate a block of memory of size ByteSize.
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
static std::shared_ptr< DataType > AllocateSharedPtrD(const DeallocatorType &d, const Args &...args)
void deallocate(pointer p, size_type n)
MemoryManager(const MemoryManager< T > &rhs)
pointer address(reference r) const
const DataType * const_pointer
const DataType & const_reference
static void RawDeallocate(DataType *array, size_t NumberOfElements)
Deallocates memory allocated from RawAllocate.
void construct(pointer p, const_reference val)
static void Deallocate(DataType *&data)
Deallocate a pointer allocated by MemoryManager::Allocate.
pointer allocate(size_type n, std::allocator< void >::const_pointer hint=0)
static DataType * Allocate(const Args &...args)
Allocates a single object from the memory pool.
const_pointer address(const_reference r) const
static DataType * RawAllocate(size_t NumberOfElements)
Allocates a chunk of raw, uninitialized memory, capable of holding NumberOfElements objects.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
bool operator==(const Array< OneD, T1 > &lhs, const Array< OneD, T2 > &rhs)
MemPool & GetMemoryPool()
bool operator!=(const Array< OneD, T1 > &lhs, const Array< OneD, T2 > &rhs)
typename abi< ScalarType, width >::type simd
Definition: tinysimd.hpp:80