DPC++ Runtime
Runtime libraries for oneAPI DPC++
accessor_iterator.hpp
Go to the documentation of this file.
1 //==------------ accessor_iterator.hpp - SYCL standard header file ---------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #pragma once
10 
11 #include <sycl/access/access.hpp> // for mode, placeholder, target
12 #include <sycl/buffer.hpp> // for range
13 #include <sycl/id.hpp> // for id
14 
15 #include <cstddef> // for size_t
16 #include <iterator> // for random_access_iterator_tag
17 #include <ostream> // for operator<<, ostream, ptrdiff_t
18 
37 
38 namespace sycl {
39 inline namespace _V1 {
40 
41 template <typename AccessorDataT, int AccessorDimensions,
43  access::placeholder IsPlaceholder, typename PropertyListT>
44 class accessor;
45 
46 namespace detail {
47 
48 template <typename DataT, int Dimensions> class accessor_iterator {
49 public:
50  using difference_type = std::ptrdiff_t;
51  using value_type = DataT;
52  // FIXME: this should likely include address space
53  using pointer = DataT *;
54  using reference = DataT &;
55  using iterator_category = std::random_access_iterator_tag;
56 
57  accessor_iterator() = default;
58 
59  reference operator*() const {
60  return *(MDataPtr + getAbsoluteOffsetToBuffer());
61  }
62 
64  ++MLinearId;
65  return *this;
66  }
67 
69  auto Old = *this;
70  ++(*this);
71  return Old;
72  }
73 
75  --MLinearId;
76  return *this;
77  }
78 
80  auto Old = *this;
81  --(*this);
82  return Old;
83  }
84 
86  MLinearId += N;
87 
88  return *this;
89  }
90 
92  auto Ret = *this;
93  Ret += N;
94  return Ret;
95  }
96 
98  const accessor_iterator &Rhs) {
99  auto Ret = Rhs;
100  Ret += N;
101  return Ret;
102  }
103 
105  MLinearId -= N;
106 
107  return *this;
108  }
109 
111  auto Temp = *this;
112  return Temp -= N;
113  }
114 
116  auto Copy = *this;
117  Copy += N;
118  return *Copy;
119  }
120 
121  bool operator<(const accessor_iterator &Other) const {
122  return MLinearId < Other.MLinearId;
123  }
124 
125  bool operator>(const accessor_iterator &Other) const { return Other < *this; }
126 
127  bool operator<=(const accessor_iterator &Other) const {
128  return !(*this > Other);
129  }
130 
131  bool operator>=(const accessor_iterator &Other) const {
132  return !(*this < Other);
133  }
134 
135  bool operator==(const accessor_iterator &Other) const {
136  return MLinearId == Other.MLinearId;
137  }
138 
139  bool operator!=(const accessor_iterator &Other) const {
140  return !(*this == Other);
141  }
142 
144  return MLinearId - Rhs.MLinearId;
145  }
146 
147 private:
148  template <typename AccessorDataT, int AccessorDimensions,
149  access::mode AccessMode, access::target AccessTarget,
150  access::placeholder IsPlaceholder, typename PropertyListT>
151  friend class sycl::accessor;
152 
153  DataT *MDataPtr = nullptr;
154 
155  // Stores a linear id of an accessor's buffer element the iterator points to.
156  // This id is relative to a range accessible through an accessor, i.e. it is
157  // limited by a space with top left corner defiend as accessor::get_offset()
158  // and bottom right corner defined as accesor::get_range().
159  size_t MLinearId = 0;
160 
161  // Describes range of linear IDs accessible by the iterator. MEnd corresponds
162  // to ID of en element past the last accessible element of accessors's
163  // buffer.
164  size_t MBegin = 0;
165  size_t MEnd = 0;
166 
167  // If set to true, then it indicates that accessor has its offset and/or range
168  // set to non-zero, i.e. it is a ranged accessor.
169  bool MAccessorIsRanged = false;
170 
171  // Fields below are used (and changed to be non-zero) only if we deal with
172  // a ranged accessor.
173  //
174  // TODO: consider making their existance dependable on Dimensions template
175  // parameter, because not all of them are needed for all possible dimensions.
176 
177  // Three field below allow us to calculate an absolute offset to an accessor's
178  // buffer to correctly identify a memory region which this iterator should
179  // point to. Comments below describe them using an iterator to the following
180  // accessor as an example:
181  //
182  // buffer<int, 2> buf(input.data(), range<2>{5, 5});
183  // auto acc = buf.get_access(range<2>{3, 3}, id<2>{1, 1});
184  //
185  // Such combination of buffer size, access range and offset is visualized
186  // below. Dot (.) symbols represent buffer elements NOT reacheable by the
187  // accessor; X symbols represent buffer elements which ARE reachable by the
188  // the accessor.
189  //
190  // . . . . .
191  // . X X X .
192  // . X X X .
193  // . X X X .
194  // . . . . .
195  //
196  // MStaticOffset stores a number of elements which precede the first
197  // accessible element, calculated as if the buffer was linearized.
198  // For the example above, MStaticOffset would be equal to 6, because
199  // there is one full row before the first accessible element and a one more on
200  // the second line. "Static" in the name highlights that this is a constant
201  // element in an equation which calculates an absoulte offset to an accessor's
202  // buffer, it doesn't depend on the current state of the iterator.
203  //
204  // NOTE: MStaticOffset is set to 0 in 1D case even if the accessor was
205  // created with offset: it is done to further optimize 1D case by
206  // incorporating that offset into MLinearId right away.
207  //
208  // MPerRowOffset stores a number of _inaccessible_ elements in each
209  // _accessible_ row. For the example above it would be equal to 2 (leftmost
210  // and the rightmost elements of a row).
211  //
212  // MPerSliceOffset stores a number of _inaccessible_ elements in each
213  // _accessible_ slice. Slice here means a single 2D layer in a 3D buffer. For
214  // the example above it would be equal to 0, because we are not looking at a
215  // 3D buffer. However, if we had two slices like visualized above,
216  // MPerSliceOffset would be equal to 16 (elements on the "perimeter" of the
217  // slice, i.e. ones represented as dots (.)).
218 
219  size_t MStaticOffset = 0;
220  size_t MPerRowOffset = 0;
221  size_t MPerSliceOffset = 0;
222 
223  // Contains a number of _accessible_ elements in a row
224  size_t MRowSize = 0;
225  // Contains a number of _accessible_ elements in a slice
226  size_t MSliceSize = 0;
227 
228  // MLinearId stores an offset which is relative to the accessible range of
229  // the accessor, which means that it could be the case that MlinearId equal
230  // to 0 should not correspond to the beginning of the underlying buffer, but
231  // instead should be re-adjusted to account for an offset passed to the
232  // accessor constructor.
233  //
234  // This function performs necessary calculations to make sure that all
235  // access ranges and offsets are taken into account.
236  size_t getAbsoluteOffsetToBuffer() const {
237  // For 1D case, any possible offsets are already incorporated into
238  // MLinearId, so 1D is always treated as a non-ranged accessor
239  if (!MAccessorIsRanged || Dimensions == 1)
240  return MLinearId;
241 
242  // Here we need to deal with 2D or 3D ranged accessor.
243  // MLinearId points to an element relative to the accessible range. It
244  // should be adjusted to account for elements which are outside of the
245  // accessible range of the accessor.
246 
247  // We start with static offset: that is a number of elements in full rows
248  // and full slices before the first accessible element.
249  size_t AbsoluteId = MLinearId + MStaticOffset;
250 
251  // Then we account for inaccessible elements in each full slice
252  size_t Remaining = MLinearId;
253  if constexpr (Dimensions == 3) {
254  AbsoluteId += MPerSliceOffset * (Remaining / MSliceSize);
255  Remaining %= MSliceSize;
256  }
257 
258  // Then we account for inaccessible elements in each full row
259  AbsoluteId += MPerRowOffset * (Remaining / MRowSize);
260  Remaining %= MRowSize;
261 
262  return AbsoluteId;
263  }
264 
265  accessor_iterator(DataT *DataPtr, const range<Dimensions> &MemoryRange,
266  const range<Dimensions> &AccessRange,
267  const id<Dimensions> &Offset)
268  : MDataPtr(DataPtr) {
269  constexpr int XIndex = Dimensions - 1;
270  constexpr int YIndex = Dimensions - 2;
271  (void)YIndex;
272  constexpr int ZIndex = Dimensions - 3;
273  (void)ZIndex;
274 
275  if constexpr (Dimensions > 1)
276  MRowSize = AccessRange[XIndex];
277  if constexpr (Dimensions > 2)
278  MSliceSize = AccessRange[YIndex] * MRowSize;
279 
280  if (id<Dimensions>{} != Offset)
281  MAccessorIsRanged = true;
282  else {
283  for (size_t I = 0; I < Dimensions; ++I)
284  if (AccessRange[I] != MemoryRange[I])
285  MAccessorIsRanged = true;
286  }
287 
288  if (MAccessorIsRanged) {
289  if constexpr (Dimensions > 2) {
290  MStaticOffset +=
291  MemoryRange[XIndex] * MemoryRange[YIndex] * Offset[ZIndex];
292  MPerSliceOffset =
293  MemoryRange[XIndex] * MemoryRange[YIndex] - MSliceSize;
294  }
295  if constexpr (Dimensions > 1) {
296  // Elements in fully inaccessible rows
297  MStaticOffset += MemoryRange[XIndex] * Offset[YIndex];
298  MPerRowOffset = MemoryRange[XIndex] - MRowSize;
299  }
300 
301  // Elements from the first accessible row
302  if constexpr (Dimensions == 1)
303  // To further optimize 1D case, offset is already included into Begin
304  MBegin = Offset[XIndex];
305  else
306  MStaticOffset += Offset[XIndex];
307  }
308 
309  MEnd = MBegin + AccessRange.size();
310  }
311 
312  static accessor_iterator getBegin(DataT *DataPtr,
313  const range<Dimensions> &MemoryRange,
314  const range<Dimensions> &AccessRange,
315  const id<Dimensions> &Offset) {
316  auto It = accessor_iterator(DataPtr, MemoryRange, AccessRange, Offset);
317  It.MLinearId = It.MBegin;
318  return It;
319  }
320 
321  static accessor_iterator getEnd(DataT *DataPtr,
322  const range<Dimensions> &MemoryRange,
323  const range<Dimensions> &AccessRange,
324  const id<Dimensions> &Offset) {
325  auto It = accessor_iterator(DataPtr, MemoryRange, AccessRange, Offset);
326  It.MLinearId = It.MEnd;
327  return It;
328  }
329 
330 public:
331 #ifndef NDEBUG
332  // Could be useful for debugging, but not a part of the official API,
333  // therefore only available in builds with assertions enabled.
334  friend std::ostream &operator<<(std::ostream &os,
335  const accessor_iterator &it) {
336  os << "accessor_iterator {\n";
337  os << "\tMLinearId: " << it.MLinearId << "\n";
338  os << "\tMEnd: " << it.MEnd << "\n";
339  os << "\tMStaticOffset: " << it.MStaticOffset << "\n";
340  os << "\tMPerRowOffset: " << it.MPerRowOffset << "\n";
341  os << "\tMPerSliceOffset: " << it.MPerSliceOffset << "\n";
342  os << "\tMRowSize: " << it.MRowSize << "\n";
343  os << "\tMSliceSize: " << it.MSliceSize << "\n";
344  os << "\tMAccessorIsRanged: " << it.MAccessorIsRanged << "\n";
345  os << "}";
346  return os;
347  }
348 #endif // NDEBUG
349 };
350 } // namespace detail
351 } // namespace _V1
352 } // namespace sycl
accessor_iterator & operator-=(difference_type N)
bool operator==(const accessor_iterator &Other) const
accessor_iterator & operator+=(difference_type N)
accessor_iterator operator-(difference_type N) const
accessor_iterator operator+(difference_type N) const
bool operator>=(const accessor_iterator &Other) const
friend std::ostream & operator<<(std::ostream &os, const accessor_iterator &it)
reference & operator[](difference_type N) const
friend accessor_iterator operator+(difference_type N, const accessor_iterator &Rhs)
bool operator<(const accessor_iterator &Other) const
difference_type operator-(const accessor_iterator &Rhs) const
bool operator>(const accessor_iterator &Other) const
bool operator!=(const accessor_iterator &Other) const
std::random_access_iterator_tag iterator_category
bool operator<=(const accessor_iterator &Other) const
A unique identifier of an item in an index space.
Definition: id.hpp:36
class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(accessor) accessor accessor(buffer< DataT, Dimensions, AllocatorT >) -> accessor< DataT, Dimensions, access::mode::read_write, target::device, access::placeholder::true_t >
Buffer accessor.
constexpr if(sizeof(T)==8)
class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(local_accessor) local_accessor class __SYCL_EBO __SYCL_SPECIAL_CLASS Dimensions
Definition: accessor.hpp:3233
class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(local_accessor) local_accessor class __SYCL_EBO __SYCL_SPECIAL_CLASS IsPlaceholder
Definition: accessor.hpp:3234
class __SYCL_EBO __SYCL_SPECIAL_CLASS __SYCL_TYPE(local_accessor) local_accessor class __SYCL_EBO __SYCL_SPECIAL_CLASS AccessMode
Definition: accessor.hpp:3233
Definition: access.hpp:18