Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
host_storage.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <uni20/config.hpp>
8
9#include <algorithm>
10#include <cstddef>
11#include <cstring>
12#include <limits>
13#include <memory>
14#include <new>
15#include <utility>
16
17namespace uni20
18{
19
26template <typename ElementType> class HostBuffer {
27 public:
28 using value_type = ElementType;
29 using size_type = std::size_t;
31 using const_iterator = value_type const*;
32
34
35 HostBuffer() noexcept = default;
36
41
45 : data_(allocate(size, initialization)), size_(size)
46 {
47 try
48 {
49 this->initialize_elements(data_, size_, initialization);
50 }
51 catch (...)
52 {
53 release(data_, size_);
54 throw;
55 }
56 }
57
58#if UNI20_HAS_MPREAL
60 HostBuffer(size_type size, StorageInitialization initialization, Precision precision)
61 requires has_runtime_precision_v<value_type>
63 {
64 precision_.value = precision;
65 this->initialize_elements(data_, size_, initialization);
66 }
67
69 Precision default_precision() const
71 {
72 return precision_.get();
73 }
74 std::optional<Precision> default_precision_if_set() const noexcept
76 {
77 return precision_.value;
78 }
80 void default_precision(Precision precision)
81 requires has_runtime_precision_v<value_type>
82 {
83 precision_.value = precision;
84 }
85#endif
86
88 HostBuffer(size_type size, value_type const& value) : data_(allocate(size)), size_(size)
89 {
90 try
91 {
92#if UNI20_HAS_MPREAL
93 if constexpr (has_runtime_precision_v<value_type>) precision_.value = value.precision();
94#endif
95 std::fill_n(data_, size_, value);
96 }
97 catch (...)
98 {
99 release(data_, size_);
100 data_ = nullptr;
101 size_ = 0;
102 throw;
103 }
104 }
105
106 HostBuffer(HostBuffer const& other) : data_(allocate(other.size_)), size_(other.size_), precision_(other.precision_)
107 {
108 try
109 {
110 this->copy_from(other.data_, other.size_);
111 }
112 catch (...)
113 {
114 release(data_, size_);
115 data_ = nullptr;
116 size_ = 0;
117 throw;
118 }
119 }
120
121 HostBuffer(HostBuffer&& other) noexcept
122 : data_(std::exchange(other.data_, nullptr)), size_(std::exchange(other.size_, 0)),
123 precision_(std::move(other.precision_))
124 {}
125
126 ~HostBuffer() { release(data_, size_); }
127
128 auto operator=(HostBuffer const& other) -> HostBuffer&
129 {
130 if (this == &other) return *this;
131 HostBuffer replacement(other);
132 this->swap(replacement);
133 return *this;
134 }
135
136 auto operator=(HostBuffer&& other) noexcept -> HostBuffer&
137 {
138 if (this == &other) return *this;
139 release(data_, size_);
140 data_ = std::exchange(other.data_, nullptr);
141 size_ = std::exchange(other.size_, 0);
142 precision_ = std::move(other.precision_);
143 return *this;
144 }
145
149
152 {
153 if (size == size_) return;
154
155 value_type* replacement = allocate(size, initialization);
156 size_type const copied_size = std::min(size_, size);
157 try
158 {
159 copy_values(replacement, data_, copied_size);
160 if (size > copied_size)
161 this->initialize_elements(replacement + copied_size, size - copied_size, initialization);
162 }
163 catch (...)
164 {
165 release(replacement, size);
166 throw;
167 }
168
169 release(data_, size_);
170 data_ = replacement;
171 size_ = size;
172 }
173
174 void clear() noexcept
175 {
176 release(data_, size_);
177 data_ = nullptr;
178 size_ = 0;
179 }
180
181 void swap(HostBuffer& other) noexcept
182 {
183 using std::swap;
184 swap(data_, other.data_);
185 swap(size_, other.size_);
186 swap(precision_, other.precision_);
187 }
188
189 [[nodiscard]] auto data() noexcept -> value_type* { return data_; }
190 [[nodiscard]] auto data() const noexcept -> value_type const* { return data_; }
191 [[nodiscard]] auto size() const noexcept -> size_type { return size_; }
192 [[nodiscard]] bool empty() const noexcept { return size_ == 0; }
193
194 [[nodiscard]] auto begin() noexcept -> iterator { return data_; }
195 [[nodiscard]] auto begin() const noexcept -> const_iterator { return data_; }
196 [[nodiscard]] auto end() noexcept -> iterator { return size_ == 0 ? data_ : data_ + size_; }
197 [[nodiscard]] auto end() const noexcept -> const_iterator { return size_ == 0 ? data_ : data_ + size_; }
198
199 [[nodiscard]] auto operator[](size_type index) noexcept -> value_type& { return data_[index]; }
200 [[nodiscard]] auto operator[](size_type index) const noexcept -> value_type const& { return data_[index]; }
201
202 private:
203 void initialize_elements(value_type* data, size_type count, StorageInitialization initialization)
204 {
205#if UNI20_HAS_MPREAL
206 if constexpr (has_runtime_precision_v<value_type>)
207 {
208 if (initialization == StorageInitialization::Uninitialized)
209 {
210 for (size_type i = 0; i < count; ++i)
212 }
213 else if (count != 0)
214 {
215 auto p = this->default_precision();
216 for (size_type i = 0; i < count; ++i)
217 data[i] = value_type(p);
218 }
219 }
220 else
221#endif
222 detail::initialize_host_elements(data, count, initialization);
223 }
224
225 static auto allocate(size_type size,
227 {
228 if (size == 0) return nullptr;
229 if (size > std::numeric_limits<size_type>::max() / sizeof(value_type)) throw std::bad_array_new_length{};
230
231 constexpr size_type alignment = std::max<size_type>(64, alignof(value_type));
232 auto* result = static_cast<value_type*>(detail::allocate_raw(sizeof(value_type) * size, alignment));
233 if constexpr (!uninitialized_ok<value_type>)
234 {
235 try
236 {
237 if constexpr (has_runtime_precision_v<value_type>)
238 std::uninitialized_fill_n(result, size, value_type(uninitialized));
239 else
240 std::uninitialized_value_construct_n(result, size);
241 }
242 catch (...)
243 {
244 detail::aligned_deleter<value_type>{}(result);
245 throw;
246 }
247 }
248 else if constexpr (!std::is_assignable_v<value_type&, value_type>)
249 {
250 if (initialization == StorageInitialization::Zero)
251 {
252 // Such elements cannot be assigned by initialize_host_elements.
253 try
254 {
255 if constexpr (detail::zero_initializable_v<value_type>)
256 {
257 for (size_type i = 0; i < size; ++i)
258 ::new (static_cast<void*>(result + i)) value_type(detail::zero_value<value_type>());
259 }
260 else
261 {
262 throw std::invalid_argument("Zero initialization requires an element type with a zero/default value");
263 }
264 }
265 catch (...)
266 {
267 detail::aligned_deleter<value_type>{}(result);
268 throw;
269 }
270 }
271 }
272 return result;
273 }
274
275 static void release(value_type* data, size_type size) noexcept
276 {
277 if (data == nullptr) return;
278 if constexpr (!uninitialized_ok<value_type>)
279 {
280 std::destroy_n(data, size);
281 }
282 detail::aligned_deleter<value_type>{}(data);
283 }
284
285 static void copy_values(value_type* output, value_type const* input, size_type size)
286 {
287 if (size == 0) return;
288 if constexpr (uninitialized_ok<value_type>)
289 {
290 // Copy the representation, including undefined-value shadow, even for
291 // trivially copyable elements that do not have an assignment operator.
292 std::memcpy(static_cast<void*>(output), input, size * sizeof(value_type));
293 }
294 else
295 {
296 std::copy_n(input, size, output);
297 }
298 }
299
300 void copy_from(value_type const* input, size_type size) { copy_values(data_, input, size); }
301
302 value_type* data_ = nullptr;
303 size_type size_ = 0;
304 [[no_unique_address]] detail::precision_default<value_type> precision_{};
305};
306
307template <typename ElementType> void swap(HostBuffer<ElementType>& lhs, HostBuffer<ElementType>& rhs) noexcept
308{
309 lhs.swap(rhs);
310}
311
316{
318 static constexpr std::size_t allocation_alignment = 64;
319
320 template <typename ElementType> using storage_t = HostBuffer<ElementType>;
321
323 template <typename ElementType>
324 [[nodiscard]] static auto make_storage(std::size_t size,
326 {
327 return storage_t<ElementType>(size, initialization);
328 }
329
330#if UNI20_HAS_MPREAL
331 template <typename ElementType>
332 requires has_runtime_precision_v<ElementType>
333 [[nodiscard]] static auto make_storage(std::size_t size, StorageInitialization initialization,
334 Precision p) -> storage_t<ElementType>
335 {
336 return storage_t<ElementType>(size, initialization, p);
337 }
338 template <typename ElementType>
339 requires has_runtime_precision_v<ElementType>
340 [[nodiscard]] static auto make_storage_like(storage_t<ElementType> const& source, std::size_t size,
341 StorageInitialization initialization) -> storage_t<ElementType>
342 {
343 if (auto p = source.default_precision_if_set()) return storage_t<ElementType>(size, initialization, *p);
344 return storage_t<ElementType>(size, initialization);
345 }
346#endif
347
348 template <typename ElementType> static auto make_handle(storage_t<ElementType>& storage) noexcept -> ElementType*
349 {
350 return storage.data();
351 }
352
353 template <typename ElementType>
354 static auto make_handle(storage_t<ElementType> const& storage) noexcept -> ElementType const*
355 {
356 return storage.data();
357 }
358
360#if UNI20_BACKEND_BLAS || UNI20_HAS_MPLAPACK_MPREAL || UNI20_HAS_MPLAPACK_FLOAT80
362#endif
364
366 [[nodiscard]] static constexpr auto backend_selector() noexcept -> backend_selector_type
367 {
369#if UNI20_BACKEND_BLAS || UNI20_HAS_MPLAPACK_MPREAL || UNI20_HAS_MPLAPACK_FLOAT80
371#endif
373 }
374};
375
376} // namespace uni20
Aligned, (un)initialized and temporary buffer allocation utilities.
Ordered backend selector values shared by tensor storage and linalg dispatch.
Owning contiguous host buffer used by HostStorage.
Definition host_storage.hpp:26
HostBuffer(HostBuffer &&other) noexcept
Definition host_storage.hpp:121
auto begin() const noexcept -> const_iterator
Definition host_storage.hpp:195
HostBuffer() noexcept=default
void resize(size_type size)
Replace the allocation while preserving the common prefix.
Definition host_storage.hpp:148
auto operator[](size_type index) noexcept -> value_type &
Definition host_storage.hpp:199
auto size() const noexcept -> size_type
Definition host_storage.hpp:191
value_type const * const_iterator
Definition host_storage.hpp:31
auto end() noexcept -> iterator
Definition host_storage.hpp:196
auto data() noexcept -> value_type *
Definition host_storage.hpp:189
HostBuffer(HostBuffer const &other)
Definition host_storage.hpp:106
value_type * iterator
Definition host_storage.hpp:30
HostBuffer(size_type size, StorageInitialization initialization)
Allocate elements with an explicit numerical initialization policy.
Definition host_storage.hpp:44
void clear() noexcept
Definition host_storage.hpp:174
void swap(HostBuffer &other) noexcept
Definition host_storage.hpp:181
~HostBuffer()
Definition host_storage.hpp:126
ElementType value_type
Definition host_storage.hpp:28
auto end() const noexcept -> const_iterator
Definition host_storage.hpp:197
auto data() const noexcept -> value_type const *
Definition host_storage.hpp:190
auto operator[](size_type index) const noexcept -> value_type const &
Definition host_storage.hpp:200
bool empty() const noexcept
Definition host_storage.hpp:192
auto operator=(HostBuffer const &other) -> HostBuffer &
Definition host_storage.hpp:128
static constexpr bool initializes_elements
Definition host_storage.hpp:33
auto begin() noexcept -> iterator
Definition host_storage.hpp:194
HostBuffer(size_type size, value_type const &value)
Allocate and fill storage with one value.
Definition host_storage.hpp:88
auto operator=(HostBuffer &&other) noexcept -> HostBuffer &
Definition host_storage.hpp:136
void resize(size_type size, StorageInitialization initialization)
Preserve the common prefix and initialize only newly added elements.
Definition host_storage.hpp:151
std::size_t size_type
Definition host_storage.hpp:29
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
Whether raw allocation may establish storage for T without construction.
Definition common/initialization.hpp:46
locale_t value
Definition data_table.cpp:49
Configured gateway to Uni20's mdspan implementation.
Definition data_table.cpp:8
Definition async/async.hpp:26
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
constexpr bool has_runtime_precision_v
Whether numerical values of this scalar family carry runtime precision.
Definition runtime_precision.hpp:10
void swap(HostBuffer< ElementType > &lhs, HostBuffer< ElementType > &rhs) noexcept
Definition host_storage.hpp:307
std::ptrdiff_t size_type
Signed size and index type, used for tensor extents and indexing.
Definition types.hpp:18
StorageInitialization
Numerical initialization requested from a storage implementation.
Definition common/initialization.hpp:26
table_alignment alignment
Definition presentation.cpp:2018
Default pageable-host tensor storage policy.
Definition host_storage.hpp:316
static constexpr std::size_t allocation_alignment
Guaranteed byte alignment of every HostBuffer allocation.
Definition host_storage.hpp:318
static auto make_handle(storage_t< ElementType > const &storage) noexcept -> ElementType const *
Definition host_storage.hpp:354
static auto make_storage(std::size_t size, StorageInitialization initialization) -> storage_t< ElementType >
Allocate host elements with the requested numerical initialization.
Definition host_storage.hpp:324
static constexpr auto backend_selector() noexcept -> backend_selector_type
Return the default ordered linalg backends for pageable host storage.
Definition host_storage.hpp:366
static auto make_handle(storage_t< ElementType > &storage) noexcept -> ElementType *
Definition host_storage.hpp:348
Backend family for BLAS dense linalg kernels, including MPLAPACK scalar adapters.
Definition backend_selector.hpp:27
Backend value for the reference host CPU dense linalg oracle.
Definition backend_selector.hpp:55
Backend family for configured dense LAPACK kernels, including MPLAPACK scalar adapters.
Definition backend_selector.hpp:20
Ordered list of backend values tried by the dispatch walk.
Definition backend_selector.hpp:62