Orrery
A GPU-accelerated N-body gravitational simulator
Loading...
Searching...
No Matches
aligned_allocator.hpp
Go to the documentation of this file.
1#pragma once
2
26
27#include <cstddef>
28#include <limits>
29#include <new>
30#include <type_traits>
31
32namespace orrery::core {
33
41inline constexpr std::size_t kCacheLineBytes = 64;
42
51template<typename T> class AlignedAllocator {
52public:
53 using value_type = T;
54
58
61 using is_always_equal = std::true_type;
62
63 static constexpr std::size_t kAlignment = kCacheLineBytes;
64
65 static_assert(kAlignment >= alignof(T),
66 "The requested alignment is weaker than the type's own requirement");
67
68 AlignedAllocator() noexcept = default;
69
72 template<typename U>
73 constexpr AlignedAllocator(const AlignedAllocator<U>& /*other*/) noexcept {}
74
81 [[nodiscard]] static T* allocate(std::size_t count) {
82 if (count > max_size()) {
83 // The multiplication below would wrap, and a wrapped byte count
84 // allocates a small block that is then handed back as though it
85 // were large. That is a silent buffer overflow rather than a failed
86 // allocation, so the overflow is turned into the failure it is.
87 throw std::bad_array_new_length{};
88 }
89
90 // The aligned form of operator new rather than a hand-rolled
91 // over-allocate-and-offset scheme: it is the standard's own answer to
92 // this question, it is available on all three supported compilers, and
93 // it needs no bookkeeping to recover the original pointer.
94 return static_cast<T*>(::operator new(count * sizeof(T), std::align_val_t{kAlignment}));
95 }
96
97 static void deallocate(T* pointer, std::size_t /*count*/) noexcept {
98 // The aligned form, matching the allocation above. An ordinary operator
99 // delete would be undefined behaviour here, since the storage did not
100 // come from an ordinary operator new.
101 //
102 // The sized overload is deliberately not used, although the count is to
103 // hand. It saves the allocator a size lookup, but it is only declared
104 // by compilers built with sized deallocation enabled, which is not the
105 // default in every release this project supports. A hint worth that
106 // little is not worth a portability condition.
107 ::operator delete(pointer, std::align_val_t{kAlignment});
108 }
109
111 [[nodiscard]] static constexpr std::size_t max_size() noexcept {
112 return std::numeric_limits<std::size_t>::max() / sizeof(T);
113 }
114};
115
118template<typename T, typename U>
119[[nodiscard]] constexpr bool operator==(const AlignedAllocator<T>& /*lhs*/,
120 const AlignedAllocator<U>& /*rhs*/) noexcept {
121 return true;
122}
123
124} // namespace orrery::core
constexpr bool operator==(const AlignedAllocator< T > &, const AlignedAllocator< U > &) noexcept
All instances are interchangeable, whatever their value type, because the allocator holds nothing tha...
Definition aligned_allocator.hpp:119
constexpr std::size_t kCacheLineBytes
The cache line length on the target machine, in bytes.
Definition aligned_allocator.hpp:41
A standard allocator that returns cache-line-aligned storage.
Definition aligned_allocator.hpp:51
static constexpr std::size_t max_size() noexcept
The largest count that can be expressed in bytes without wrapping.
Definition aligned_allocator.hpp:111
std::true_type propagate_on_container_move_assignment
Moving a container moves its storage rather than copying its elements, which is the behaviour the con...
Definition aligned_allocator.hpp:57
std::true_type is_always_equal
The allocator holds no state, so any instance can free memory that any other instance allocated.
Definition aligned_allocator.hpp:61
static T * allocate(std::size_t count)
Allocate storage for count objects.
Definition aligned_allocator.hpp:81