sycl-vector.hh
Go to the documentation of this file.
1#pragma once
2#include <cassert>
3#include <batchlas/export.hh>
6
7/// @file
8/// @brief batchlas::UnifiedVector, an owning growable array in USM shared memory.
9
10namespace batchlas {
11
12/// @brief Owning, growable array of `T` in USM shared memory (`sycl::malloc_shared`).
13///
14/// The storage is allocated in the shared context of Device::default_device(),
15/// so it is readable and writable from the host and from that device. Copying
16/// deep-copies on the host; moving transfers ownership. Converts implicitly to
17/// Span<T>, which is how it is passed to entry points. Growth reallocates, so
18/// any Span or pointer taken earlier is invalidated.
19/// @tparam T element type (trivially copyable: growth uses memcpy)
20/// @ingroup api_matrix
21template <typename T>
22struct BATCHLAS_API UnifiedVector
23{
24 using value_type = T; ///< Element type.
25 using pointer = T*; ///< Pointer to an element.
26 using size_t = std::size_t; ///< Size and index type.
27 /// @brief Allocates @p size uninitialised elements.
28 /// @throws std::bad_alloc if the allocation fails
30 /// @brief Allocates @p size elements set to @p value (filled on the host).
31 UnifiedVector(size_t size, T value);
32 /// @brief Deep copy of @p other's first `size()` elements into a new allocation.
34 /// @brief Deep copy of @p other's elements; reallocates only when this capacity is smaller.
35 ///
36 /// Trivially copyable `T` is copied with a device memcpy that is waited on.
38 /// @brief Frees the allocation; never throws, even if the runtime reports an earlier async error.
40
41 /// @brief Sets the size; reallocates (keeping the contents) only when @p new_size exceeds the capacity.
42 void resize(size_t new_size);
43 /// @brief Grows to @p new_size, setting the new elements to @p value.
44 /// @note A no-op unless @p new_size exceeds the capacity: it neither shrinks nor
45 /// grows within the existing capacity (unlike the one-argument resize()).
46 void resize(size_t new_size, T value);
47 /// @brief Grows a ring buffer to @p new_size, unrolling its live segment to the front.
48 ///
49 /// The live data starts at @p front and ends with the @p seg_size elements at
50 /// @p back, wrapping when back < front. After the call it is contiguous from
51 /// index 0 and the rest is value-initialised. No-op unless @p new_size exceeds
52 /// the capacity.
53 void resize(size_t new_size, size_t front, size_t back, size_t seg_size);
54
55 /// @brief Ensures a capacity of at least @p new_capacity, keeping the contents and the size.
56 void reserve(size_t new_capacity);
57
58 /// @brief An empty vector with no allocation.
59 UnifiedVector() : size_(0), capacity_(0), data_(nullptr) {}
60 /// @brief Takes @p other's allocation and leaves @p other empty.
61 UnifiedVector(UnifiedVector<T> &&other) : size_(other.size_), capacity_(other.capacity_), data_(other.data_) {
62 other.size_ = 0;
63 other.capacity_ = 0;
64 other.data_ = nullptr;
65 }
66 /// @brief Frees this vector's allocation, takes @p other's and leaves @p other empty.
68 if (this == &other) return *this;
69 UnifiedVector<T> previous(std::move(*this)); // its destructor frees the old allocation
70 this->data_ = other.data_;
71 this->size_ = other.size_;
72 this->capacity_ = other.capacity_;
73 other.data_ = nullptr;
74 other.size_ = 0;
75 other.capacity_ = 0;
76 return *this;
77 }
78
79 /// @brief A Span over the first `size()` elements.
80 inline constexpr operator Span<T>() const { return Span<T>(data_, size_); }
81 /// @brief A Span over the first `size()` elements.
82 inline constexpr Span<T> to_span() const { return Span<T>(data_, size_); }
83 /// @brief A Span over elements [@p offset, @p offset + @p count); not bounds-checked.
84 inline constexpr Span<T> subspan(size_t offset, size_t count) const { return Span<T>(data_ + offset, count); }
85 /// @brief A Span over elements [@p offset, size()).
86 inline constexpr Span<T> subspan(size_t offset) const { return Span<T>(data_ + offset, size_ - offset); }
87 /// @brief Sets every element to @p data, on the host.
88 inline constexpr void fill(T data) { std::fill(begin(), end(), data); }
89 inline constexpr T *data() const { return data_; } ///< Pointer to the first element (null when never allocated).
90 inline constexpr size_t size() const { return size_; } ///< Number of elements.
91 inline constexpr size_t capacity() const { return capacity_; } ///< Allocated elements.
92
93
94 /// @brief Sets the size to 0 and keeps the allocation.
95 inline constexpr void clear() { size_ = 0; }
96
97 /// @brief Element @p index; prints and asserts (debug build) when out of range.
98 inline constexpr T &operator[](size_t index) { if(index >= size_) printf("Index: %zu, Size: %zu\n", index, size_); assert (index < size_); return data_[index]; }
99 /// @brief Element @p index (const); same checks as the non-const overload.
100 inline constexpr const T &operator[](size_t index) const { if (index >= size_) printf("Index: %zu, Size: %zu\n", index, size_); assert (index < size_); return data_[index]; }
101
102 /// @brief Element @p index. @pre index < size() (asserted in debug builds, not thrown)
103 inline constexpr T &at(size_t index) { assert(index < size_); return data_[index]; }
104 /// @brief Element @p index (const). @pre index < size()
105 inline constexpr const T &at(size_t index) const { assert(index < size_); return data_[index]; }
106
107 /// @brief Element-wise comparison with Span::operator== semantics.
108 inline constexpr bool operator==(const UnifiedVector<T> &other) const {
109 return Span<T>(*this) == Span<T>(other);
110 }
111
112 /// @brief Appends @p value, doubling the capacity when full (which invalidates earlier Spans).
113 inline constexpr void push_back(const T &value) {
114 if(size_ == capacity_){
115 size_t new_capacity = capacity_ == 0 ? 1 : 2*capacity_;
116 reserve(new_capacity);
117 }
118 data_[size_++] = value;
119 }
120
121 /// @brief Appends @p value by move; growth as for the copying overload.
122 inline constexpr void push_back(T &&value) {
123 if(size_ == capacity_){
124 size_t new_capacity = capacity_ == 0 ? 1 : 2*capacity_;
125 reserve(new_capacity);
126 }
127 data_[size_++] = std::move(value);
128 }
129
130
131 /// @brief Removes and returns the last element; keeps the capacity. @pre size() > 0
132 inline constexpr T pop_back() { assert(size_ > 0); return data_[--size_]; }
133
134 /// @brief Prints the elements on the host as `[a, b, c]`, like Span's operator<<.
135 template <typename U>
136 friend std::ostream &operator<<(std::ostream &os, const UnifiedVector<U> &vec);
137
138 inline constexpr T *begin() const { return data_; } ///< Iterator to the first element.
139 inline constexpr T *end() const { return data_ + size_; } ///< Iterator one past the last element.
140
141 inline constexpr T &back() const { return data_[size_ - 1]; } ///< Last element. @pre size() > 0
142 inline constexpr T &front() const { return data_[0]; } ///< First element. @pre size() > 0
143
144 /// @brief Exchanges storage, size and capacity with @p other; no element is copied.
145 inline constexpr void swap(UnifiedVector<T> &other) {
146 std::swap(data_, other.data_);
147 std::swap(size_, other.size_);
148 std::swap(capacity_, other.capacity_);
149 }
150private:
151 // USM shared allocation of @p n elements on the default device; throws std::bad_alloc on a
152 // null return (n > 0), so a caller updates capacity_ only after it succeeded.
153 static T* alloc_shared(size_t n);
154
155 size_t size_;
156 size_t capacity_;
157 pointer data_;
158};
159
160/// @brief Swaps the storage of two vectors without copying; found by ADL.
161/// @ingroup api_matrix_lowlevel
162template <typename T>
163inline constexpr void swap(UnifiedVector<T> &lhs, UnifiedVector<T> &rhs) {
164 lhs.swap(rhs);
165}
166
167} // namespace batchlas
168
169// Transitional shim: UnifiedVector used to live at global scope; define
170// BATCHLAS_NO_GLOBAL_NAMES to drop it. swap() is deliberately not shimmed: ADL
171// finds it, and a global `swap` is exactly the collision the move removed.
172#ifndef BATCHLAS_NO_GLOBAL_NAMES
174#endif
constexpr void swap(UnifiedVector< T > &lhs, UnifiedVector< T > &rhs)
Swaps the storage of two vectors without copying; found by ADL.
Non-owning view of size() contiguous elements of type T.
Definition sycl-span.hh:39
Owning, growable array of T in USM shared memory (sycl::malloc_shared).
constexpr Span< T > subspan(size_t offset, size_t count) const
A Span over elements [offset, offset + count); not bounds-checked.
constexpr T & front() const
First element.
constexpr Span< T > to_span() const
A Span over the first size() elements.
constexpr void clear()
Sets the size to 0 and keeps the allocation.
UnifiedVector< T > & operator=(UnifiedVector< T > &&other)
Frees this vector's allocation, takes other's and leaves other empty.
UnifiedVector< T > & operator=(const UnifiedVector< T > &other)
Deep copy of other's elements; reallocates only when this capacity is smaller.
T * pointer
Pointer to an element.
constexpr T & at(size_t index)
Element index.
void resize(size_t new_size)
Sets the size; reallocates (keeping the contents) only when new_size exceeds the capacity.
UnifiedVector()
An empty vector with no allocation.
constexpr T & back() const
Last element.
UnifiedVector(size_t size, T value)
Allocates size elements set to value (filled on the host).
void reserve(size_t new_capacity)
Ensures a capacity of at least new_capacity, keeping the contents and the size.
constexpr bool operator==(const UnifiedVector< T > &other) const
Element-wise comparison with Span::operator== semantics.
constexpr T & operator[](size_t index)
Element index; prints and asserts (debug build) when out of range.
friend std::ostream & operator<<(std::ostream &os, const UnifiedVector< U > &vec)
Prints the elements on the host as [a, b, c], like Span's operator<<.
UnifiedVector(const UnifiedVector< T > &other)
Deep copy of other's first size() elements into a new allocation.
constexpr void push_back(const T &value)
Appends value, doubling the capacity when full (which invalidates earlier Spans).
constexpr T * begin() const
Iterator to the first element.
constexpr void fill(T data)
Sets every element to data, on the host.
constexpr Span< T > subspan(size_t offset) const
A Span over elements [offset, size()).
void resize(size_t new_size, size_t front, size_t back, size_t seg_size)
Grows a ring buffer to new_size, unrolling its live segment to the front.
constexpr T pop_back()
Removes and returns the last element; keeps the capacity.
constexpr T * end() const
Iterator one past the last element.
void resize(size_t new_size, T value)
Grows to new_size, setting the new elements to value.
UnifiedVector(size_t size)
Allocates size uninitialised elements.
constexpr size_t capacity() const
Allocated elements.
constexpr size_t size() const
Number of elements.
T value_type
Element type.
UnifiedVector(UnifiedVector< T > &&other)
Takes other's allocation and leaves other empty.
constexpr void swap(UnifiedVector< T > &other)
Exchanges storage, size and capacity with other; no element is copied.
constexpr T * data() const
Pointer to the first element (null when never allocated).
constexpr const T & at(size_t index) const
Element index (const).
std::size_t size_t
Size and index type.
~UnifiedVector()
Frees the allocation; never throws, even if the runtime reports an earlier async error.
constexpr void push_back(T &&value)
Appends value by move; growth as for the copying overload.
constexpr const T & operator[](size_t index) const
Element index (const); same checks as the non-const overload.
batchlas::Device, batchlas::Event and batchlas::Queue: where work runs and how it is ordered.
batchlas::Span, the non-owning pointer + length every entry point takes for 1-D data.