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
55namespace 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
82template <typename DataType> class MemoryManager
83{
84public:
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) = delete;
251 {
252 }
253
255 {
256 return &r;
257 }
259 {
260 return &r;
261 }
262
264 std::allocator<void>::const_pointer hint =
265 0) // typename MemoryManager<void>::pointer hint = 0)
266 {
267 boost::ignore_unused(hint);
268 return RawAllocate(n);
269 }
270
272 {
273 return RawDeallocate(p, n);
274 }
275
277 {
278 new (p) DataType(val);
279 }
280
282 {
283 p->~DataType();
284 }
285
287 {
288 return std::numeric_limits<size_type>::max() / sizeof(DataType);
289 }
290
291 template <typename U> struct rebind
292 {
294 };
295
296 /////////////////////////////////////////////////////////////////
297 ///@}
298 /////////////////////////////////////////////////////////////////
299
300private:
301};
302
303template <typename DataType>
305 const MemoryManager<DataType> &rhs)
306{
307 boost::ignore_unused(lhs, rhs);
308 return true;
309}
310
311template <typename DataType>
313 const MemoryManager<DataType> &rhs)
314{
315 return !(lhs == rhs);
316}
317
318} // namespace Nektar
319
320#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.
pointer allocate(size_type n, std::allocator< void >::const_pointer hint=0)
const_pointer address(const_reference r) const
static DataType * Allocate(const Args &...args)
Allocates a single object from the memory pool.
std::vector< double > d(NPUPPER *NPUPPER)
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