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
46
47#include <vector>
48
49#ifdef max
50#undef max
51#endif
52
53namespace Nektar
54{
55
56/// @brief General purpose memory allocation routines with the ability
57/// to allocate from thread specific memory pools.
58///
59/// If compiled with NEKTAR_MEMORY_POOL_ENABLED, the MemoryManager
60/// allocates from thread specific memory pools for small objects.
61/// Large objects are managed with the system supplied new/delete.
62/// These memory pools provide faster allocation and deallocation
63/// of small objects (particularly useful for shared pointers which
64/// allocate many 4 byte objects).
65///
66/// @warning All memory allocated from the memory manager must be returned
67/// to the memory manager. Calling delete on memory allocated from the
68/// manager will likely cause undefined behavior. A particularly subtle
69/// violation of this rule occurs when giving memory allocated from the
70/// manager to a shared pointer.
71/// @code
72/// std::shared_ptr<Obj> f(MemoryManager<Obj>::Allocate());
73/// @endcode
74/// Shared pointers call delete when they go out of scope, so this line of
75/// code will cause problems. Instead, you should call the
76/// AllocateSharedPtr method:
77/// @code
78/// std::shared_ptr<Obj> f = MemoryManager<Obj>::AllocateSharedPtr();
79/// @endcode
80template <typename DataType> class MemoryManager
81{
82public:
83 /// @brief Deallocate a pointer allocated by
84 /// MemoryManager::Allocate.
85 /// @note Results are undefined if called with a pointer to
86 /// something that was not allocated with the memory manager.
87 ///
88 /// Use this method to deallocate a pointer you have allocated from
89 /// the MemoryManager using the Allocate method.
90 ///
91 /// Example:
92 /// @code
93 /// CustObj* c = MemoryManager::Allocate<CustObj>();
94 /// MemoryManager::Deallocate(c);
95 /// @endcode
96 static void Deallocate(DataType *&data)
97 {
98#ifdef NEKTAR_MEMORY_POOL_ENABLED
99 data->~DataType();
100 GetMemoryPool().Deallocate(data, sizeof(DataType));
101#else
102#ifdef NEKTAR_USE_ALIGNED_MEM
103 boost::alignment::aligned_free(data);
104#else
105 delete data;
106#endif
107#endif
108
109 data = nullptr;
110 }
111
112#ifdef NEKTAR_MEMORY_POOL_ENABLED
113 /// @brief Allocates a single object from the memory pool.
114 /// @throws unknown If the object throws an exception during
115 /// construction, this method will catch it, release the memory
116 /// back to the pool, then rethrow it.
117 ///
118 /// The allocated object must be returned to the memory pool
119 /// via Deallocate.
120 template <typename... Args> static DataType *Allocate(const Args &...args)
121 {
122 DataType *result =
123 static_cast<DataType *>(GetMemoryPool().Allocate(sizeof(DataType)));
124
125 if (result)
126 {
127 try
128 {
129 new (result) DataType(args...);
130 }
131 catch (...)
132 {
133 GetMemoryPool().Deallocate(result, sizeof(DataType));
134 throw;
135 }
136 }
137
138 return result;
139 }
140
141#else // NEKTAR_MEMORY_POOL_ENABLED
142 /// @brief Allocates a single object from the memory pool.
143 /// @throws unknown Any exception thrown by DataType's default
144 /// constructor will propogate through this method.
145 ///
146 /// The allocated object must be returned to the memory pool
147 /// via Deallocate.
148 template <typename... Args> static DataType *Allocate(const Args &...args)
149 {
150#ifdef NEKTAR_USE_ALIGNED_MEM
151 void *ptr = boost::alignment::aligned_alloc(
152 tinysimd::simd<NekDouble>::alignment, sizeof(DataType));
153 return new (ptr) DataType(args...);
154#else
155 return new DataType(args...);
156#endif
157 }
158#endif // NEKTAR_MEMORY_POOL_ENABLED
159
160 /// @brief Allocate a shared pointer from the memory pool.
161 ///
162 /// The shared pointer does not need to be returned to the memory
163 /// pool. When the reference count to this object reaches 0, the
164 /// shared pointer will automatically return the memory.
165 template <typename... Args>
166 static std::shared_ptr<DataType> AllocateSharedPtr(const Args &...args)
167 {
168 return AllocateSharedPtrD([](DataType *) {}, args...);
169 }
170
171 template <typename DeallocatorType, typename... Args>
172 static std::shared_ptr<DataType> AllocateSharedPtrD(
173 const DeallocatorType &d, const Args &...args)
174 {
175 DataType *data = Allocate(args...);
176 return std::shared_ptr<DataType>(data, [=](DataType *ptr) {
177 d(ptr);
179 });
180 }
181
182 /// \brief Allocates a chunk of raw, uninitialized memory, capable of
183 /// holding NumberOfElements objects.
184 ///
185 /// \param NumberOfElements The number of elements the array should be
186 /// capable of holding.
187 ///
188 /// This method is not meant to be called by client code. Use Array
189 /// instead. Any memory allocated from this method must be returned to the
190 /// memory pool via RawDeallocate. Failure to do so will result in memory
191 /// leaks and undefined behavior.
192 static DataType *RawAllocate(size_t NumberOfElements)
193 {
194#ifdef NEKTAR_MEMORY_POOL_ENABLED
195 return static_cast<DataType *>(
196 GetMemoryPool().Allocate(NumberOfElements * sizeof(DataType)));
197#else // NEKTAR_MEMORY_POOL_ENABLED
198#ifdef NEKTAR_USE_ALIGNED_MEM
199 return static_cast<DataType *>(boost::alignment::aligned_alloc(
201 NumberOfElements * sizeof(DataType)));
202#else
203 return static_cast<DataType *>(
204 ::operator new(NumberOfElements * sizeof(DataType)));
205#endif
206#endif // NEKTAR_MEMORY_POOL_ENABLED
207 }
208
209 /// \brief Deallocates memory allocated from RawAllocate.
210 /// \param array A pointer to the memory returned from RawAllocate.
211 /// \param NumberOfElements The number of object held in the array.
212 ///
213 /// This method is not meant to be called by client code. Use Array
214 /// instead. Only memory allocated via RawAllocate should be returned to the
215 /// pool here.
216 static void RawDeallocate(DataType *array,
217 [[maybe_unused]] size_t NumberOfElements)
218 {
219#ifdef NEKTAR_MEMORY_POOL_ENABLED
220 GetMemoryPool().Deallocate(array, sizeof(DataType) * NumberOfElements);
221#else // NEKTAR_MEMORY_POOL_ENABLED
222#ifdef NEKTAR_USE_ALIGNED_MEM
223 boost::alignment::aligned_free(array);
224#else
225 ::operator delete(array);
226#endif
227#endif // NEKTAR_MEMORY_POOL_ENABLED
228 }
229
230 /////////////////////////////////////////////////////////////////
231 ///\name Allocator Interface
232 /// The allocator interface allows a MemoryManager object to be used
233 /// in any object that allows an allocator parameter, such as STL
234 /// containers.
235 /////////////////////////////////////////////////////////////////
236 typedef DataType value_type;
237 typedef size_t size_type;
238 typedef ptrdiff_t difference_type;
239 typedef DataType *pointer;
240 typedef const DataType *const_pointer;
241 typedef DataType &reference;
242 typedef const DataType &const_reference;
243
245 {
246 }
247 template <typename T> MemoryManager(const MemoryManager<T> &rhs) = delete;
249 {
250 }
251
253 {
254 return &r;
255 }
257 {
258 return &r;
259 }
260
262 {
263 return RawAllocate(n);
264 }
265
267 {
268 return RawDeallocate(p, n);
269 }
270
272 {
273 new (p) DataType(val);
274 }
275
277 {
278 p->~DataType();
279 }
280
282 {
283 return std::numeric_limits<size_type>::max() / sizeof(DataType);
284 }
285
286 template <typename U> struct rebind
287 {
289 };
290
291 /////////////////////////////////////////////////////////////////
292 ///@}
293 /////////////////////////////////////////////////////////////////
294
295private:
296};
297
298template <typename DataType>
299bool operator==([[maybe_unused]] const MemoryManager<DataType> &lhs,
300 [[maybe_unused]] const MemoryManager<DataType> &rhs)
301{
302 return true;
303}
304
305template <typename DataType>
307 const MemoryManager<DataType> &rhs)
308{
309 return !(lhs == rhs);
310}
311
312} // namespace Nektar
313
314#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 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.
void deallocate(pointer p, size_type n)
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.
MemoryManager(const MemoryManager< T > &rhs)=delete
void construct(pointer p, const_reference val)
static std::shared_ptr< DataType > AllocateSharedPtrD(const DeallocatorType &d, const Args &...args)
static void Deallocate(DataType *&data)
Deallocate a pointer allocated by MemoryManager::Allocate.
const_pointer address(const_reference r) const
pointer allocate(size_type n)
static DataType * Allocate(const Args &...args)
Allocates a single object from the memory pool.
std::vector< double > d(NPUPPER *NPUPPER)
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