MCKL
Monte Carlo Kernel Library
memory.hpp
Go to the documentation of this file.
1 //============================================================================
2 // MCKL/include/mckl/core/memory.hpp
3 //----------------------------------------------------------------------------
4 // MCKL: Monte Carlo Kernel Library
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2018, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef MCKL_CORE_MEMORY_HPP
33 #define MCKL_CORE_MEMORY_HPP
34 
35 #include <mckl/internal/config.h>
36 
37 #include <complex>
38 #include <cstdlib>
39 #include <cstring>
40 #include <memory>
41 #include <new>
42 #include <vector>
43 
44 #if MCKL_HAS_POSIX
45 #include <stdlib.h>
46 #endif
47 
48 #if MCKL_HAS_JEMALLOC
49 #include <jemalloc/jemalloc.h>
50 #endif
51 
52 #if MCKL_HAS_TBB
53 #include <tbb/scalable_allocator.h>
54 #endif
55 
58 #ifndef MCKL_MEMORY_TYPE
59 #if MCKL_USE_TBB_MALLOC
60 #define MCKL_MEMORY_TYPE ::mckl::MemoryTBB
61 #elif MCKL_USE_JEMALLOC
62 #define MCKL_MEMORY_TYPE ::mckl::MemoryJEM
63 #elif MCKL_HAS_POSIX
64 #define MCKL_MEMORY_TYPE ::mckl::MemorySYS
65 #else
66 #define MCKL_MEMORY_TYPE ::mckl::MemorySTD
67 #endif
68 #endif
69 
70 namespace mckl {
71 
72 namespace internal {
73 
74 template <std::size_t Alignment, typename UIntType>
75 inline constexpr std::size_t alignment_round0(UIntType n)
76 {
77  static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
78  "**alignment_round0** used with Alignment other than a power of two "
79  "positive integer");
80 
81  static_assert(Alignment >= sizeof(void *),
82  "**alignment_round0** used with Alignment less than sizeof(void *)");
83 
84  static_assert(std::is_unsigned<UIntType>::value,
85  "alignment_round0** used with UIntType other than unsigned integer "
86  "types");
87 
88  constexpr std::size_t addn = Alignment - 1;
89  constexpr std::size_t mask = ~addn;
90 
91  return (n + addn) & mask;
92 }
93 
94 template <std::size_t Alignment, typename UIntType>
95 inline constexpr std::size_t alignment_round(UIntType n)
96 {
97  return n == 0 ? Alignment : alignment_round0<Alignment>(n);
98 }
99 
100 template <typename T>
102 {
103  public:
104  static constexpr std::size_t value = std::is_scalar<T>::value ?
105  (alignof(T) > MCKL_ALIGNMENT ? alignof(T) : MCKL_ALIGNMENT) :
106  (alignof(T) > MCKL_MINIMUM_ALIGNMENT ? alignof(T) :
108 }; // class AlignOfImpl
109 
110 template <typename T>
111 class AlignOfImpl<std::complex<T>>
112 {
113  public:
114  static constexpr std::size_t value = AlignOfImpl<T>::value;
115 }; // class AlignOfImpl
116 
117 } // namespace internal
118 
128 template <typename T>
129 constexpr std::size_t AlignOf = internal::AlignOfImpl<T>::value;
130 
135 template <std::size_t Alignment>
137 {
138  static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
139  "**MemorySTD** used with Alignment other than a power of two positive "
140  "integer");
141 
142  static_assert(Alignment >= sizeof(void *),
143  "**MemorySTD** used with Alignment less than sizeof(void *)");
144 
145  public:
146  static constexpr std::size_t alignment() { return Alignment; }
147 
148  static void *allocate(std::size_t n, const void * = nullptr) noexcept
149  {
150  const std::size_t m = internal::alignment_round0<Alignment>(
151  n + sizeof(std::uintptr_t) + Alignment);
152  if (m < n) {
153  return nullptr;
154  }
155 
156  std::uintptr_t *const address =
157  static_cast<std::uintptr_t *>(std::malloc(m));
158  if (address == nullptr) {
159  return nullptr;
160  }
161 
162  std::uintptr_t *const ptr = reinterpret_cast<std::uintptr_t *>(
163  internal::alignment_round0<Alignment>(
164  reinterpret_cast<std::uintptr_t>(address + 1)));
165  ptr[-1] = reinterpret_cast<std::uintptr_t>(address);
166 
167  return static_cast<void *>(ptr);
168  }
169 
170  static void deallocate(void *ptr, std::size_t = 0) noexcept
171  {
172  if (ptr != nullptr) {
173  const std::uintptr_t address =
174  static_cast<std::uintptr_t *>(ptr)[-1];
175  std::free(reinterpret_cast<void *>(address));
176  }
177  }
178 }; // class MemorySTD
179 
180 #if MCKL_HAS_POSIX
181 
186 template <std::size_t Alignment>
188 {
189  static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
190  "**MemorySYS** used with Alignment other than a power of two positive "
191  "integer");
192 
193  static_assert(Alignment >= sizeof(void *),
194  "**MemorySYS** used with Alignment less than sizeof(void *)");
195 
196  public:
197  static constexpr std::size_t alignment() { return Alignment; }
198 
199  static void *allocate(std::size_t n, const void * = nullptr) noexcept
200  {
201  const std::size_t m = internal::alignment_round<Alignment>(n);
202 
203  if (m < n) {
204  return nullptr;
205  }
206 
207  void *ptr = nullptr;
208  if (::posix_memalign(&ptr, Alignment, m) != 0) {
209  ptr = nullptr;
210  }
211 
212  return ptr;
213  }
214 
215  static void deallocate(void *ptr, std::size_t = 0) noexcept
216  {
217  if (ptr != nullptr) {
218  ::free(ptr);
219  }
220  }
221 }; // class MemorySYS
222 
223 #endif // MCKL_HAS_POSIX
224 
225 #if MCKL_HAS_JEMALLOC
226 
231 template <std::size_t Alignment>
233 {
234  static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
235  "**MemoryJEM** used with Alignment other than a power of two positive "
236  "integer");
237 
238  static_assert(Alignment >= sizeof(void *),
239  "**MemoryJEM** used with Alignment less than sizeof(void *)");
240 
241  public:
242  static constexpr std::size_t alignment() { return Alignment; }
243 
244  static void *allocate(std::size_t n, const void * = nullptr) noexcept
245  {
246  const std::size_t m = internal::alignment_round<Alignment>(n);
247 
248  return m < n ? nullptr : ::aligned_alloc(Alignment, m);
249  }
250 
251  static void deallocate(void *ptr, std::size_t = 0) noexcept
252  {
253  if (ptr != nullptr) {
254  ::free(ptr);
255  }
256  }
257 }; // class MemoryJEM
258 
259 #endif // MCKL_HAS_JEMALLOC
260 
261 #if MCKL_HAS_TBB
262 
267 template <std::size_t Alignment>
269 {
270  static_assert(Alignment != 0 && (Alignment & (Alignment - 1)) == 0,
271  "**MemoryTBB** used with Alignment other than a power of two positive "
272  "integer");
273 
274  static_assert(Alignment >= sizeof(void *),
275  "**MemoryTBB** used with Alignment less than sizeof(void *)");
276 
277  public:
278  static constexpr std::size_t alignment() { return Alignment; }
279 
280  static void *allocate(std::size_t n, const void * = nullptr) noexcept
281  {
282  const std::size_t m = internal::alignment_round<Alignment>(n);
283 
284  return m < n ? nullptr : scalable_aligned_malloc(m, Alignment);
285  }
286 
287  static void deallocate(void *ptr, std::size_t = 0) noexcept
288  {
289  if (ptr != nullptr) {
290  scalable_aligned_free(ptr);
291  }
292  }
293 }; // class MemoryTBB
294 
295 #endif // MCKL_HAS_TBB
296 
309 template <std::size_t Alignment>
310 using Memory = MCKL_MEMORY_TYPE<Alignment>;
311 
321 template <typename T, typename Mem = Memory<AlignOf<T>>>
322 class Allocator : public std::allocator<T>
323 {
324  public:
325  template <typename U>
326  class rebind
327  {
328  public:
330  };
331 
332  Allocator() = default;
333 
334  template <typename U>
335  Allocator(const Allocator<U, Mem> &other) noexcept
336  : std::allocator<T>(static_cast<std::allocator<U>>(other))
337  {
338  }
339 
340  T *allocate(std::size_t n, const void *hint = nullptr)
341  {
342  const std::size_t m = n * sizeof(T);
343  if (m < n) {
344  throw std::bad_alloc();
345  }
346 
347  T *ptr = static_cast<T *>(Mem::allocate(m, hint));
348  if (ptr == nullptr) {
349  throw std::bad_alloc();
350  }
351 
352  return ptr;
353  }
354 
355  void deallocate(T *ptr, std::size_t size = 0) noexcept(
356  noexcept(Mem::deallocate(ptr, size)))
357  {
358  Mem::deallocate(ptr, size);
359  }
360 
361  template <typename U>
362  void construct(U *p)
363  {
364  construct_dispatch(p, std::is_scalar<U>());
365  }
366 
367  private:
368  template <typename U>
369  void construct_dispatch(U *, std::true_type)
370  {
371  }
372 
373  template <typename U>
374  void construct_dispatch(U *p, std::false_type)
375  {
376  ::new (static_cast<void *>(p)) U();
377  }
378 }; // class Allocator
379 
380 template <typename Mem>
381 class Allocator<void, Mem>
382 {
383  public:
384  using value_type = void;
385  using pointer = void *;
386  using const_pointer = const void *;
388  using is_always_equal = std::true_type;
389 
390  template <typename U>
391  class rebind
392  {
393  public:
395  };
396 }; // class Allocator
397 
398 template <typename Mem>
399 class Allocator<const void, Mem>
400 {
401  public:
402  using value_type = const void;
403  using pointer = const void *;
404  using const_pointer = const void *;
406  using is_always_equal = std::true_type;
407 
408  template <typename U>
409  class rebind
410  {
411  public:
413  };
414 }; // class Allocator
415 
417 template <typename T1, typename T2, typename Mem1, typename Mem2>
418 inline constexpr bool operator==(
419  const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
420 {
421  return std::is_same<Mem1, Mem2>::value;
422 }
423 
425 template <typename T1, typename T2, typename Mem1, typename Mem2>
426 inline constexpr bool operator!=(
427  const Allocator<T1, Mem1> &, const Allocator<T2, Mem2> &)
428 {
429  return !std::is_same<Mem1, Mem2>::value;
430 }
431 
434 template <typename T, typename Alloc = Allocator<T>>
435 using Vector = std::vector<T, Alloc>;
436 
437 } // namespace mckl
438 
439 #endif // MCKL_CORE_MEMORY_HPP
Allocator(const Allocator< U, Mem > &other) noexcept
Definition: memory.hpp:335
static void deallocate(void *ptr, std::size_t=0) noexcept
Definition: memory.hpp:287
std::vector< T, Alloc > Vector
std::vector with Allocator as the default allocator
Definition: memory.hpp:435
STL namespace.
T * allocate(std::size_t n, const void *hint=nullptr)
Definition: memory.hpp:340
static void * allocate(std::size_t n, const void *=nullptr) noexcept
Definition: memory.hpp:280
static constexpr std::size_t alignment()
Definition: memory.hpp:146
void construct(U *p)
Definition: memory.hpp:362
static void deallocate(void *ptr, std::size_t=0) noexcept
Definition: memory.hpp:215
static constexpr std::size_t value
Definition: memory.hpp:104
constexpr bool operator==(const Allocator< T1, Mem1 > &, const Allocator< T2, Mem2 > &)
Definition: memory.hpp:418
static void deallocate(void *ptr, std::size_t=0) noexcept
Definition: memory.hpp:170
void deallocate(T *ptr, std::size_t size=0) noexcept(noexcept(Mem::deallocate(ptr, size)))
Definition: memory.hpp:355
constexpr std::size_t alignment_round(UIntType n)
Definition: memory.hpp:95
constexpr std::size_t AlignOf
Alignment of types in bytes.
Definition: memory.hpp:129
static constexpr std::size_t alignment()
Definition: memory.hpp:278
#define MCKL_MINIMUM_ALIGNMENT
The minimum alignment for any type.
Definition: config.h:196
constexpr std::size_t alignment_round0(UIntType n)
Definition: memory.hpp:75
Memory allocation using the standard library.
Definition: memory.hpp:136
std::true_type is_always_equal
Definition: memory.hpp:388
Memory allocation using native system functions.
Definition: memory.hpp:187
std::true_type propagate_on_container_move_assignment
Definition: memory.hpp:405
std::true_type propagate_on_container_move_assignment
Definition: memory.hpp:387
Definition: mcmc.hpp:40
const void * const_pointer
Definition: memory.hpp:386
#define MCKL_ALIGNMENT
The default alignment for scalar type.
Definition: config.h:187
Standard library compatible allocator with policies.
Definition: memory.hpp:322
Memory allocation using jemalloc.
Definition: memory.hpp:232
Memory allocation using Intel TBB.
Definition: memory.hpp:268
static void * allocate(std::size_t n, const void *=nullptr) noexcept
Definition: memory.hpp:199
static void * allocate(std::size_t n, const void *=nullptr) noexcept
Definition: memory.hpp:148
static void * allocate(std::size_t n, const void *=nullptr) noexcept
Definition: memory.hpp:244
static constexpr std::size_t alignment()
Definition: memory.hpp:242
static void deallocate(void *ptr, std::size_t=0) noexcept
Definition: memory.hpp:251
static constexpr std::size_t alignment()
Definition: memory.hpp:197
constexpr bool operator!=(const Allocator< T1, Mem1 > &, const Allocator< T2, Mem2 > &)
Definition: memory.hpp:426