RAJA
RAJA provides a collection of platform portability abstractions for C++ HPC applications.
WorkStorage.hpp
Go to the documentation of this file.
1 
11 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
12 // Copyright (c) Lawrence Livermore National Security, LLC and other
13 // RAJA Project Developers. See top-level LICENSE and COPYRIGHT
14 // files for dates and other details. No copyright assignment is required
15 // to contribute to RAJA.
16 //
17 // SPDX-License-Identifier: (BSD-3-Clause)
18 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
19 
20 #ifndef RAJA_PATTERN_WORKGROUP_WorkStorage_HPP
21 #define RAJA_PATTERN_WORKGROUP_WorkStorage_HPP
22 
23 #include "RAJA/config.hpp"
24 
25 #include <cstddef>
26 #include <memory>
27 #include <utility>
28 #include <type_traits>
29 
30 #include "RAJA/util/Operators.hpp"
31 #include "RAJA/util/macros.hpp"
32 
34 
36 
37 namespace RAJA
38 {
39 
40 namespace detail
41 {
42 
43 // iterator class that implements the random access iterator interface
44 // in terms of a in terms of a few basic operations
45 // operator * ( )
46 // operator += ( difference_type )
47 // operator - ( iterator_base const& )
48 // operator == ( iterator_base const& )
49 // operator < ( iterator_base const& )
50 template<typename iterator_base>
51 struct random_access_iterator : iterator_base
52 {
53  using base = iterator_base;
54  using value_type = const typename base::value_type;
55  using pointer = typename base::pointer;
56  using reference = typename base::reference;
57  using difference_type = typename base::difference_type;
58  using iterator_category = std::random_access_iterator_tag;
59 
60  using base::base;
61 
64 
67 
69  {
70  return *static_cast<base const&>(*this);
71  }
72 
73  RAJA_HOST_DEVICE pointer operator->() const { return &(*(*this)); }
74 
76  {
77  random_access_iterator copy = *this;
78  copy += i;
79  return *copy;
80  }
81 
83  {
84  (*this) += 1;
85  return *this;
86  }
87 
89  {
90  random_access_iterator copy = *this;
91  ++(*this);
92  return copy;
93  }
94 
96  {
97  (*this) -= 1;
98  return *this;
99  }
100 
102  {
103  random_access_iterator copy = *this;
104  --(*this);
105  return copy;
106  }
107 
109  {
110  static_cast<base&>(*this) += rhs;
111  return *this;
112  }
113 
115  {
116  (*this) += -rhs;
117  return *this;
118  }
119 
121  random_access_iterator const& lhs,
122  difference_type rhs)
123  {
124  random_access_iterator copy = lhs;
125  copy += rhs;
126  return copy;
127  }
128 
130  difference_type lhs,
131  random_access_iterator const& rhs)
132  {
133  random_access_iterator copy = rhs;
134  copy += lhs;
135  return copy;
136  }
137 
139  random_access_iterator const& lhs,
140  difference_type rhs)
141  {
142  random_access_iterator copy = lhs;
143  copy -= rhs;
144  return copy;
145  }
146 
148  random_access_iterator const& lhs,
149  random_access_iterator const& rhs)
150  {
151  return static_cast<base const&>(lhs) - static_cast<base const&>(rhs);
152  }
153 
154  RAJA_HOST_DEVICE friend inline bool operator==(
155  random_access_iterator const& lhs,
156  random_access_iterator const& rhs)
157  {
158  return static_cast<base const&>(lhs) == static_cast<base const&>(rhs);
159  }
160 
161  RAJA_HOST_DEVICE friend inline bool operator!=(
162  random_access_iterator const& lhs,
163  random_access_iterator const& rhs)
164  {
165  return !(lhs == rhs);
166  }
167 
168  RAJA_HOST_DEVICE friend inline bool operator<(
169  random_access_iterator const& lhs,
170  random_access_iterator const& rhs)
171  {
172  return static_cast<base const&>(lhs) < static_cast<base const&>(rhs);
173  }
174 
175  RAJA_HOST_DEVICE friend inline bool operator<=(
176  random_access_iterator const& lhs,
177  random_access_iterator const& rhs)
178  {
179  return !(rhs < lhs);
180  }
181 
182  RAJA_HOST_DEVICE friend inline bool operator>(
183  random_access_iterator const& lhs,
184  random_access_iterator const& rhs)
185  {
186  return rhs < lhs;
187  }
188 
189  RAJA_HOST_DEVICE friend inline bool operator>=(
190  random_access_iterator const& lhs,
191  random_access_iterator const& rhs)
192  {
193  return !(lhs < rhs);
194  }
195 };
196 
197 
201 template<typename STORAGE_POLICY_T, typename ALLOCATOR_T, typename Dispatcher_T>
203 
204 template<typename ALLOCATOR_T, typename Dispatcher_T>
205 class WorkStorage<RAJA::array_of_pointers, ALLOCATOR_T, Dispatcher_T>
206 {
207  using allocator_traits_type = std::allocator_traits<ALLOCATOR_T>;
208  using propagate_on_container_copy_assignment =
209  typename allocator_traits_type::propagate_on_container_copy_assignment;
210  using propagate_on_container_move_assignment =
211  typename allocator_traits_type::propagate_on_container_move_assignment;
212  using propagate_on_container_swap =
213  typename allocator_traits_type::propagate_on_container_swap;
214  static_assert(
215  std::is_same<typename allocator_traits_type::value_type, char>::value,
216  "WorkStorage expects an allocator for 'char's.");
217 
218 public:
220  using dispatcher_type = Dispatcher_T;
221 
222  template<typename holder>
223  using true_value_type = WorkStruct<sizeof(holder), dispatcher_type>;
224 
226  using allocator_type = ALLOCATOR_T;
227  using size_type = std::size_t;
228  using difference_type = std::ptrdiff_t;
230  using const_reference = const value_type&;
231  using pointer = value_type*;
232  using const_pointer = const value_type*;
233 
234 private:
235  // struct used in storage vector to retain pointer and allocation size
236  struct pointer_and_size
237  {
238  pointer ptr;
239  size_type size;
240  };
241 
242 public:
243  // iterator base class for accessing stored WorkStructs outside of the
244  // container
245  struct const_iterator_base
246  {
247  using value_type = const typename WorkStorage::value_type;
248  using pointer = typename WorkStorage::const_pointer;
249  using reference = typename WorkStorage::const_reference;
250  using difference_type = typename WorkStorage::difference_type;
251  using iterator_category = std::random_access_iterator_tag;
252 
253  const_iterator_base(const pointer_and_size* ptrptr) : m_ptrptr(ptrptr) {}
254 
255  RAJA_HOST_DEVICE reference operator*() const { return *(m_ptrptr->ptr); }
256 
258  {
259  m_ptrptr += n;
260  return *this;
261  }
262 
264  const_iterator_base const& lhs_iter,
265  const_iterator_base const& rhs_iter)
266  {
267  return lhs_iter.m_ptrptr - rhs_iter.m_ptrptr;
268  }
269 
270  RAJA_HOST_DEVICE friend inline bool operator==(
271  const_iterator_base const& lhs_iter,
272  const_iterator_base const& rhs_iter)
273  {
274  return lhs_iter.m_ptrptr == rhs_iter.m_ptrptr;
275  }
276 
277  RAJA_HOST_DEVICE friend inline bool operator<(
278  const_iterator_base const& lhs_iter,
279  const_iterator_base const& rhs_iter)
280  {
281  return lhs_iter.m_ptrptr < rhs_iter.m_ptrptr;
282  }
283 
284  private:
285  const pointer_and_size* m_ptrptr;
286  };
287 
289 
290  explicit WorkStorage(allocator_type const& aloc)
291  : m_vec(0, aloc),
292  m_aloc(aloc)
293  {}
294 
295  WorkStorage(WorkStorage const&) = delete;
296  WorkStorage& operator=(WorkStorage const&) = delete;
297 
299  : m_vec(std::move(rhs.m_vec)),
300  m_aloc(std::move(rhs.m_aloc))
301  {}
302 
304  {
305  if (this != &rhs)
306  {
307  move_assign_private(std::move(rhs),
308  propagate_on_container_move_assignment {});
309  }
310  return *this;
311  }
312 
313  // reserve may be used to allocate enough memory to store num_loops
314  // and loop_storage_size is ignored in this version because each
315  // object has its own allocation
316  void reserve(size_type num_loops, size_type loop_storage_size)
317  {
318  RAJA_UNUSED_VAR(loop_storage_size);
319  m_vec.reserve(num_loops);
320  }
321 
322  // number of loops stored
323  size_type size() const { return m_vec.size(); }
324 
325  const_iterator begin() const { return const_iterator(m_vec.begin()); }
326 
327  const_iterator end() const { return const_iterator(m_vec.end()); }
328 
329  // number of bytes used for storage of loops
331  {
332  size_type storage_size_nbytes = 0;
333  for (size_t i = 0; i < m_vec.size(); ++i)
334  {
335  storage_size_nbytes += m_vec[i].size;
336  }
337  return storage_size_nbytes;
338  }
339 
340  template<typename holder, typename... holder_ctor_args>
341  void emplace(const dispatcher_type* dispatcher,
342  holder_ctor_args&&... ctor_args)
343  {
344  m_vec.emplace_back(create_value<holder>(
345  dispatcher, std::forward<holder_ctor_args>(ctor_args)...));
346  }
347 
348  // destroy all stored loops, deallocates all storage
349  void clear()
350  {
351  while (!m_vec.empty())
352  {
353  destroy_value(m_vec.back());
354  m_vec.pop_back();
355  }
356  m_vec.shrink_to_fit();
357  }
358 
359  ~WorkStorage() { clear(); }
360 
361 private:
362  RAJAVec<
363  pointer_and_size,
364  typename allocator_traits_type::template rebind_alloc<pointer_and_size>>
365  m_vec;
366  allocator_type m_aloc;
367 
368  // move assignment if allocator propagates on move assignment
369  void move_assign_private(WorkStorage&& rhs, std::true_type)
370  {
371  clear();
372  m_vec = std::move(rhs.m_vec);
373  m_aloc = std::move(rhs.m_aloc);
374  }
375 
376  // move assignment if allocator does not propagate on move assignment
377  void move_assign_private(WorkStorage&& rhs, std::false_type)
378  {
379  clear();
380  if (m_aloc == rhs.m_aloc)
381  {
382  // take storage if allocators compare equal
383  m_vec = std::move(rhs.m_vec);
384  }
385  else
386  {
387  // allocate new storage if allocators do not compare equal
388  for (size_type i = 0; i < rhs.m_vec.size(); ++i)
389  {
390  m_vec.emplace_back(move_destroy_value(std::move(rhs), rhs.m_vec[i]));
391  }
392  rhs.m_vec.clear();
393  rhs.clear();
394  }
395  }
396 
397  // allocate and construct value in storage
398  template<typename holder, typename... holder_ctor_args>
399  pointer_and_size create_value(const dispatcher_type* dispatcher,
400  holder_ctor_args&&... ctor_args)
401  {
402  const size_type value_size = sizeof(true_value_type<holder>);
403 
404  pointer value_ptr = reinterpret_cast<pointer>(
405  allocator_traits_type::allocate(m_aloc, value_size));
406 
407  value_type::template construct<holder>(
408  value_ptr, dispatcher, std::forward<holder_ctor_args>(ctor_args)...);
409 
410  return pointer_and_size {value_ptr, value_size};
411  }
412 
413  // allocate and move construct object as copy of other value and
414  // destroy and deallocate other value
415  pointer_and_size move_destroy_value(WorkStorage&& rhs,
416  pointer_and_size other_value_and_size)
417  {
418  pointer value_ptr = reinterpret_cast<pointer>(
419  allocator_traits_type::allocate(m_aloc, other_value_and_size.size));
420 
421  value_type::move_destroy(value_ptr, other_value_and_size.ptr);
422 
423  allocator_traits_type::deallocate(
424  rhs.m_aloc, reinterpret_cast<char*>(other_value_and_size.ptr),
425  other_value_and_size.size);
426 
427  return pointer_and_size {value_ptr, other_value_and_size.size};
428  }
429 
430  // destroy and deallocate value
431  void destroy_value(pointer_and_size value_and_size_ptr)
432  {
433  value_type::destroy(value_and_size_ptr.ptr);
434  allocator_traits_type::deallocate(
435  m_aloc, reinterpret_cast<char*>(value_and_size_ptr.ptr),
436  value_and_size_ptr.size);
437  }
438 };
439 
440 template<typename ALLOCATOR_T, typename Dispatcher_T>
441 class WorkStorage<RAJA::ragged_array_of_objects, ALLOCATOR_T, Dispatcher_T>
442 {
443  using allocator_traits_type = std::allocator_traits<ALLOCATOR_T>;
444  using propagate_on_container_copy_assignment =
445  typename allocator_traits_type::propagate_on_container_copy_assignment;
446  using propagate_on_container_move_assignment =
447  typename allocator_traits_type::propagate_on_container_move_assignment;
448  using propagate_on_container_swap =
449  typename allocator_traits_type::propagate_on_container_swap;
450  static_assert(
451  std::is_same<typename allocator_traits_type::value_type, char>::value,
452  "WorkStorage expects an allocator for 'char's.");
453 
454 public:
456  using dispatcher_type = Dispatcher_T;
457 
458  template<typename holder>
459  using true_value_type = WorkStruct<sizeof(holder), dispatcher_type>;
460 
462  using allocator_type = ALLOCATOR_T;
463  using size_type = std::size_t;
464  using difference_type = std::ptrdiff_t;
466  using const_reference = const value_type&;
467  using pointer = value_type*;
468  using const_pointer = const value_type*;
469 
470  // iterator base class for accessing stored WorkStructs outside of the
471  // container
472  struct const_iterator_base
473  {
474  using value_type = const typename WorkStorage::value_type;
475  using pointer = typename WorkStorage::const_pointer;
476  using reference = typename WorkStorage::const_reference;
477  using difference_type = typename WorkStorage::difference_type;
478  using iterator_category = std::random_access_iterator_tag;
479 
480  const_iterator_base(const char* array_begin, const size_type* offset_iter)
481  : m_array_begin(array_begin),
482  m_offset_iter(offset_iter)
483  {}
484 
486  {
487  return *reinterpret_cast<pointer>(m_array_begin + *m_offset_iter);
488  }
489 
491  {
492  m_offset_iter += n;
493  return *this;
494  }
495 
497  const_iterator_base const& lhs_iter,
498  const_iterator_base const& rhs_iter)
499  {
500  return lhs_iter.m_offset_iter - rhs_iter.m_offset_iter;
501  }
502 
503  RAJA_HOST_DEVICE friend inline bool operator==(
504  const_iterator_base const& lhs_iter,
505  const_iterator_base const& rhs_iter)
506  {
507  return lhs_iter.m_offset_iter == rhs_iter.m_offset_iter;
508  }
509 
510  RAJA_HOST_DEVICE friend inline bool operator<(
511  const_iterator_base const& lhs_iter,
512  const_iterator_base const& rhs_iter)
513  {
514  return lhs_iter.m_offset_iter < rhs_iter.m_offset_iter;
515  }
516 
517  private:
518  const char* m_array_begin;
519  const size_type* m_offset_iter;
520  };
521 
523 
524  explicit WorkStorage(allocator_type const& aloc)
525  : m_offsets(0, aloc),
526  m_aloc(aloc)
527  {}
528 
529  WorkStorage(WorkStorage const&) = delete;
530  WorkStorage& operator=(WorkStorage const&) = delete;
531 
533  : m_offsets(std::move(rhs.m_offsets)),
534  m_array_begin(rhs.m_array_begin),
535  m_array_end(rhs.m_array_end),
536  m_array_cap(rhs.m_array_cap),
537  m_aloc(std::move(rhs.m_aloc))
538  {
539  rhs.m_array_begin = nullptr;
540  rhs.m_array_end = nullptr;
541  rhs.m_array_cap = nullptr;
542  }
543 
545  {
546  if (this != &rhs)
547  {
548  move_assign_private(std::move(rhs),
549  propagate_on_container_move_assignment {});
550  }
551  return *this;
552  }
553 
554  // reserve space for num_loops in the array of offsets
555  // and space for loop_storage_size bytes of loop storage
556  void reserve(size_type num_loops, size_type loop_storage_size)
557  {
558  m_offsets.reserve(num_loops);
559  array_reserve(loop_storage_size);
560  }
561 
562  // number of loops stored
563  size_type size() const { return m_offsets.size(); }
564 
566  {
567  return const_iterator(m_array_begin, m_offsets.begin());
568  }
569 
571  {
572  return const_iterator(m_array_begin, m_offsets.end());
573  }
574 
575  // number of bytes used for storage of loops
576  size_type storage_size() const { return m_array_end - m_array_begin; }
577 
578  template<typename holder, typename... holder_ctor_args>
579  void emplace(const dispatcher_type* dispatcher,
580  holder_ctor_args&&... ctor_args)
581  {
582  size_type value_offset = storage_size();
583  size_type value_size = create_value<holder>(
584  value_offset, dispatcher, std::forward<holder_ctor_args>(ctor_args)...);
585  m_offsets.emplace_back(value_offset);
586  m_array_end += value_size;
587  }
588 
589  // destroy loops and deallocate all storage
590  void clear()
591  {
592  array_clear();
593  if (m_array_begin != nullptr)
594  {
595  allocator_traits_type::deallocate(m_aloc, m_array_begin,
596  storage_capacity());
597  m_array_begin = nullptr;
598  m_array_end = nullptr;
599  m_array_cap = nullptr;
600  }
601  }
602 
603  ~WorkStorage() { clear(); }
604 
605 private:
606  RAJAVec<size_type,
607  typename allocator_traits_type::template rebind_alloc<size_type>>
608  m_offsets;
609  char* m_array_begin = nullptr;
610  char* m_array_end = nullptr;
611  char* m_array_cap = nullptr;
612  allocator_type m_aloc;
613 
614  // move assignment if allocator propagates on move assignment
615  void move_assign_private(WorkStorage&& rhs, std::true_type)
616  {
617  clear();
618 
619  m_offsets = std::move(rhs.m_offsets);
620  m_array_begin = rhs.m_array_begin;
621  m_array_end = rhs.m_array_end;
622  m_array_cap = rhs.m_array_cap;
623  m_aloc = std::move(rhs.m_aloc);
624 
625  rhs.m_array_begin = nullptr;
626  rhs.m_array_end = nullptr;
627  rhs.m_array_cap = nullptr;
628  }
629 
630  // move assignment if allocator does not propagate on move assignment
631  void move_assign_private(WorkStorage&& rhs, std::false_type)
632  {
633  clear();
634  if (m_aloc == rhs.m_aloc)
635  {
636 
637  m_offsets = std::move(rhs.m_offsets);
638  m_array_begin = rhs.m_array_begin;
639  m_array_end = rhs.m_array_end;
640  m_array_cap = rhs.m_array_cap;
641 
642  rhs.m_array_begin = nullptr;
643  rhs.m_array_end = nullptr;
644  rhs.m_array_cap = nullptr;
645  }
646  else
647  {
648  array_reserve(rhs.storage_size());
649 
650  for (size_type i = 0; i < rhs.size(); ++i)
651  {
652  m_array_end = m_array_begin + rhs.m_offsets[i];
653  move_destroy_value(m_array_end, rhs.m_array_begin + rhs.m_offsets[i]);
654  m_offsets.emplace_back(rhs.m_offsets[i]);
655  }
656  m_array_end = m_array_begin + rhs.storage_size();
657  rhs.m_array_end = rhs.m_array_begin;
658  rhs.m_offsets.clear();
659  rhs.clear();
660  }
661  }
662 
663  // get loop storage capacity, used and unused in bytes
664  size_type storage_capacity() const { return m_array_cap - m_array_begin; }
665 
666  // get unused loop storage capacity in bytes
667  size_type storage_unused() const { return m_array_cap - m_array_end; }
668 
669  // reserve space for loop_storage_size bytes of loop storage
670  void array_reserve(size_type loop_storage_size)
671  {
672  if (loop_storage_size > storage_capacity())
673  {
674 
675  char* new_array_begin =
676  allocator_traits_type::allocate(m_aloc, loop_storage_size);
677  char* new_array_end = new_array_begin + storage_size();
678  char* new_array_cap = new_array_begin + loop_storage_size;
679 
680  for (size_type i = 0; i < size(); ++i)
681  {
682  move_destroy_value(new_array_begin + m_offsets[i],
683  m_array_begin + m_offsets[i]);
684  }
685 
686  if (m_array_begin != nullptr)
687  {
688  allocator_traits_type::deallocate(m_aloc, m_array_begin,
689  storage_capacity());
690  }
691 
692  m_array_begin = new_array_begin;
693  m_array_end = new_array_end;
694  m_array_cap = new_array_cap;
695  }
696  }
697 
698  // destroy loop objects (does not deallocate array storage)
699  void array_clear()
700  {
701  while (!m_offsets.empty())
702  {
703  destroy_value(m_offsets.back());
704  m_array_end = m_array_begin + m_offsets.back();
705  m_offsets.pop_back();
706  }
707  m_offsets.shrink_to_fit();
708  }
709 
710  // ensure there is enough storage to hold the next loop body at value offset
711  // and store the loop body
712  template<typename holder, typename... holder_ctor_args>
713  size_type create_value(size_type value_offset,
714  const dispatcher_type* dispatcher,
715  holder_ctor_args&&... ctor_args)
716  {
717  const size_type value_size = sizeof(true_value_type<holder>);
718 
719  if (value_size > storage_unused())
720  {
721  array_reserve(
722  std::max(storage_size() + value_size, 2 * storage_capacity()));
723  }
724 
725  pointer value_ptr = reinterpret_cast<pointer>(m_array_begin + value_offset);
726 
727  value_type::template construct<holder>(
728  value_ptr, dispatcher, std::forward<holder_ctor_args>(ctor_args)...);
729 
730  return value_size;
731  }
732 
733  // move construct the loop body into value from other, and destroy the
734  // loop body in other
735  void move_destroy_value(char* value_ptr, char* other_value_ptr)
736  {
737  value_type::move_destroy(reinterpret_cast<pointer>(value_ptr),
738  reinterpret_cast<pointer>(other_value_ptr));
739  }
740 
741  // destroy the loop body at value offset
742  void destroy_value(size_type value_offset)
743  {
744  pointer value_ptr = reinterpret_cast<pointer>(m_array_begin + value_offset);
745  value_type::destroy(value_ptr);
746  }
747 };
748 
749 template<typename ALLOCATOR_T, typename Dispatcher_T>
751  ALLOCATOR_T,
752  Dispatcher_T>
753 {
754  using allocator_traits_type = std::allocator_traits<ALLOCATOR_T>;
755  using propagate_on_container_copy_assignment =
756  typename allocator_traits_type::propagate_on_container_copy_assignment;
757  using propagate_on_container_move_assignment =
758  typename allocator_traits_type::propagate_on_container_move_assignment;
759  using propagate_on_container_swap =
760  typename allocator_traits_type::propagate_on_container_swap;
761  static_assert(
762  std::is_same<typename allocator_traits_type::value_type, char>::value,
763  "WorkStorage expects an allocator for 'char's.");
764 
765 public:
767  using dispatcher_type = Dispatcher_T;
768 
769  template<typename holder>
770  using true_value_type = WorkStruct<sizeof(holder), dispatcher_type>;
771 
773  using allocator_type = ALLOCATOR_T;
774  using size_type = std::size_t;
775  using difference_type = std::ptrdiff_t;
777  using const_reference = const value_type&;
778  using pointer = value_type*;
779  using const_pointer = const value_type*;
780 
781  // iterator base class for accessing stored WorkStructs outside of the
782  // container
783  struct const_iterator_base
784  {
785  using value_type = const typename WorkStorage::value_type;
786  using pointer = typename WorkStorage::const_pointer;
787  using reference = typename WorkStorage::const_reference;
788  using difference_type = typename WorkStorage::difference_type;
789  using iterator_category = std::random_access_iterator_tag;
790 
791  const_iterator_base(const char* array_pos, size_type stride)
792  : m_array_pos(array_pos),
793  m_stride(stride)
794  {}
795 
797  {
798  return *reinterpret_cast<const value_type*>(m_array_pos);
799  }
800 
802  {
803  m_array_pos += n * m_stride;
804  return *this;
805  }
806 
808  const_iterator_base const& lhs_iter,
809  const_iterator_base const& rhs_iter)
810  {
811  return (lhs_iter.m_array_pos - rhs_iter.m_array_pos) / lhs_iter.m_stride;
812  }
813 
814  RAJA_HOST_DEVICE friend inline bool operator==(
815  const_iterator_base const& lhs_iter,
816  const_iterator_base const& rhs_iter)
817  {
818  return lhs_iter.m_array_pos == rhs_iter.m_array_pos;
819  }
820 
821  RAJA_HOST_DEVICE friend inline bool operator<(
822  const_iterator_base const& lhs_iter,
823  const_iterator_base const& rhs_iter)
824  {
825  return lhs_iter.m_array_pos < rhs_iter.m_array_pos;
826  }
827 
828  private:
829  const char* m_array_pos;
830  size_type m_stride;
831  };
832 
834 
835  explicit WorkStorage(allocator_type const& aloc) : m_aloc(aloc) {}
836 
837  WorkStorage(WorkStorage const&) = delete;
838  WorkStorage& operator=(WorkStorage const&) = delete;
839 
841  : m_aloc(std::move(rhs.m_aloc)),
842  m_stride(rhs.m_stride),
843  m_array_begin(rhs.m_array_begin),
844  m_array_end(rhs.m_array_end),
845  m_array_cap(rhs.m_array_cap)
846  {
847  // do not reset stride, leave it for reuse
848  rhs.m_array_begin = nullptr;
849  rhs.m_array_end = nullptr;
850  rhs.m_array_cap = nullptr;
851  }
852 
854  {
855  if (this != &rhs)
856  {
857  move_assign_private(std::move(rhs),
858  propagate_on_container_move_assignment {});
859  }
860  return *this;
861  }
862 
863  // reserve space for at least loop_storage_size bytes of loop storage
864  // and num_loops at current stride
865  void reserve(size_type num_loops, size_type loop_storage_size)
866  {
867  size_type num_storage_loops =
868  std::max(num_loops, (loop_storage_size + m_stride - 1) / m_stride);
869  array_reserve(num_storage_loops * m_stride, m_stride);
870  }
871 
872  // number of loops stored
873  size_type size() const { return storage_size() / m_stride; }
874 
876  {
877  return const_iterator(m_array_begin, m_stride);
878  }
879 
880  const_iterator end() const { return const_iterator(m_array_end, m_stride); }
881 
882  // amount of storage in bytes used to store loops
883  size_type storage_size() const { return m_array_end - m_array_begin; }
884 
885  template<typename holder, typename... holder_ctor_args>
886  void emplace(const dispatcher_type* dispatcher,
887  holder_ctor_args&&... ctor_args)
888  {
889  create_value<holder>(dispatcher,
890  std::forward<holder_ctor_args>(ctor_args)...);
891  m_array_end += m_stride;
892  }
893 
894  // destroy stored loop bodies and deallocates all storage
895  void clear()
896  {
897  array_clear();
898  if (m_array_begin != nullptr)
899  {
900  allocator_traits_type::deallocate(m_aloc, m_array_begin,
901  storage_capacity());
902  m_array_begin = nullptr;
903  m_array_end = nullptr;
904  m_array_cap = nullptr;
905  }
906  }
907 
908  ~WorkStorage() { clear(); }
909 
910 private:
911  allocator_type m_aloc;
912  size_type m_stride = 1; // can't be 0 because size divides stride
913  char* m_array_begin = nullptr;
914  char* m_array_end = nullptr;
915  char* m_array_cap = nullptr;
916 
917  // move assignment if allocator propagates on move assignment
918  void move_assign_private(WorkStorage&& rhs, std::true_type)
919  {
920  clear();
921 
922  m_aloc = std::move(rhs.m_aloc);
923  m_stride = rhs.m_stride;
924  m_array_begin = rhs.m_array_begin;
925  m_array_end = rhs.m_array_end;
926  m_array_cap = rhs.m_array_cap;
927 
928  // do not reset stride, leave it for reuse
929  rhs.m_array_begin = nullptr;
930  rhs.m_array_end = nullptr;
931  rhs.m_array_cap = nullptr;
932  }
933 
934  // move assignment if allocator does not propagate on move assignment
935  void move_assign_private(WorkStorage&& rhs, std::false_type)
936  {
937  clear();
938  if (m_aloc == rhs.m_aloc)
939  {
940 
941  m_stride = rhs.m_stride;
942  m_array_begin = rhs.m_array_begin;
943  m_array_end = rhs.m_array_end;
944  m_array_cap = rhs.m_array_cap;
945 
946  // do not reset stride, leave it for reuse
947  rhs.m_array_begin = nullptr;
948  rhs.m_array_end = nullptr;
949  rhs.m_array_cap = nullptr;
950  }
951  else
952  {
953 
954  m_stride = rhs.m_stride;
955  array_reserve(rhs.storage_size(), rhs.m_stride);
956 
957  for (size_type i = 0; i < rhs.size(); ++i)
958  {
959  move_destroy_value(m_array_end, rhs.m_array_begin + i * rhs.m_stride);
960  m_array_end += m_stride;
961  }
962  rhs.m_array_end = rhs.m_array_begin;
963  rhs.clear();
964  }
965  }
966 
967  // storage capacity, used and unused, in bytes
968  size_type storage_capacity() const { return m_array_cap - m_array_begin; }
969 
970  // unused storage capacity in bytes
971  size_type storage_unused() const { return m_array_cap - m_array_end; }
972 
973  // allocate enough storage for loop_storage_size bytes with
974  // each loop body separated by new_stride bytes
975  // note that this can reallocate storage with or without changing
976  // the storage stride
977  // Note that loop_storage_size must be a multiple of new_stride
978  void array_reserve(size_type loop_storage_size, size_type new_stride)
979  {
980  if (loop_storage_size > storage_capacity() || new_stride > m_stride)
981  {
982 
983  char* new_array_begin =
984  allocator_traits_type::allocate(m_aloc, loop_storage_size);
985  char* new_array_end = new_array_begin + size() * new_stride;
986  char* new_array_cap = new_array_begin + loop_storage_size;
987 
988  for (size_type i = 0; i < size(); ++i)
989  {
990  move_destroy_value(new_array_begin + i * new_stride,
991  m_array_begin + i * m_stride);
992  }
993 
994  if (m_array_begin != nullptr)
995  {
996  allocator_traits_type::deallocate(m_aloc, m_array_begin,
997  storage_capacity());
998  }
999 
1000  m_stride = new_stride;
1001  m_array_begin = new_array_begin;
1002  m_array_end = new_array_end;
1003  m_array_cap = new_array_cap;
1004  }
1005  }
1006 
1007  // destroy the loops in storage (does not deallocate loop storage)
1008  void array_clear()
1009  {
1010  for (size_type value_offset = storage_size(); value_offset > 0;
1011  value_offset -= m_stride)
1012  {
1013  destroy_value(value_offset - m_stride);
1014  m_array_end -= m_stride;
1015  }
1016  }
1017 
1018  // ensure there is enough storage to store the loop body
1019  // and construct the body in storage.
1020  template<typename holder, typename... holder_ctor_args>
1021  void create_value(const dispatcher_type* dispatcher,
1022  holder_ctor_args&&... ctor_args)
1023  {
1024  const size_type value_size = sizeof(true_value_type<holder>);
1025 
1026  if (value_size > storage_unused() && value_size <= m_stride)
1027  {
1028  array_reserve(std::max(storage_size() + m_stride, 2 * storage_capacity()),
1029  m_stride);
1030  }
1031  else if (value_size > m_stride)
1032  {
1033  array_reserve((size() + 1) * value_size, value_size);
1034  }
1035 
1036  size_type value_offset = storage_size();
1037  pointer value_ptr = reinterpret_cast<pointer>(m_array_begin + value_offset);
1038 
1039  value_type::template construct<holder>(
1040  value_ptr, dispatcher, std::forward<holder_ctor_args>(ctor_args)...);
1041  }
1042 
1043  // move construct the loop body in value from other and
1044  // destroy the loop body in other
1045  void move_destroy_value(char* value_ptr, char* other_value_ptr)
1046  {
1047  value_type::move_destroy(reinterpret_cast<pointer>(value_ptr),
1048  reinterpret_cast<pointer>(other_value_ptr));
1049  }
1050 
1051  // destroy the loop body at value offset
1052  void destroy_value(size_type value_offset)
1053  {
1054  pointer value_ptr = reinterpret_cast<pointer>(m_array_begin + value_offset);
1055  value_type::destroy(value_ptr);
1056  }
1057 };
1058 
1059 } // namespace detail
1060 
1061 } // namespace RAJA
1062 
1063 #endif // closing endif for header file include guard
Header file for RAJA operator definitions.
RAJA header file for simple vector template class that enables RAJA to be used with or without the C+...
Header file providing RAJA WorkStruct for workgroup.
Class template that provides a simple vector implementation sufficient to insulate RAJA entities from...
Definition: RAJAVec.hpp:56
size_type size() const
Definition: RAJAVec.hpp:199
const_iterator begin() const
Definition: WorkStorage.hpp:325
WorkStorage(allocator_type const &aloc)
Definition: WorkStorage.hpp:290
WorkStorage & operator=(WorkStorage &&rhs)
Definition: WorkStorage.hpp:303
void emplace(const dispatcher_type *dispatcher, holder_ctor_args &&... ctor_args)
Definition: WorkStorage.hpp:341
void reserve(size_type num_loops, size_type loop_storage_size)
Definition: WorkStorage.hpp:316
const_iterator end() const
Definition: WorkStorage.hpp:327
void emplace(const dispatcher_type *dispatcher, holder_ctor_args &&... ctor_args)
Definition: WorkStorage.hpp:886
void reserve(size_type num_loops, size_type loop_storage_size)
Definition: WorkStorage.hpp:865
WorkStorage & operator=(WorkStorage &&rhs)
Definition: WorkStorage.hpp:853
void reserve(size_type num_loops, size_type loop_storage_size)
Definition: WorkStorage.hpp:556
void emplace(const dispatcher_type *dispatcher, holder_ctor_args &&... ctor_args)
Definition: WorkStorage.hpp:579
WorkStorage(allocator_type const &aloc)
Definition: WorkStorage.hpp:524
WorkStorage & operator=(WorkStorage &&rhs)
Definition: WorkStorage.hpp:544
Definition: WorkStorage.hpp:202
Header file for common RAJA internal macro definitions.
RAJA_HOST_DEVICE RAJA_INLINE void RAJA_UNUSED_VAR(T &&...) noexcept
Definition: macros.hpp:120
#define RAJA_HOST_DEVICE
Definition: macros.hpp:65
Definition: AlignedRangeIndexSetBuilders.cpp:35
RAJA_HOST_DEVICE constexpr RAJA_INLINE Result max(Args... args)
Definition: foldl.hpp:155
Definition: ListSegment.hpp:416
typename WorkStorage::const_pointer pointer
Definition: WorkStorage.hpp:248
typename WorkStorage::const_reference reference
Definition: WorkStorage.hpp:249
typename WorkStorage::difference_type difference_type
Definition: WorkStorage.hpp:250
RAJA_HOST_DEVICE reference operator*() const
Definition: WorkStorage.hpp:255
RAJA_HOST_DEVICE const_iterator_base & operator+=(difference_type n)
Definition: WorkStorage.hpp:257
RAJA_HOST_DEVICE friend bool operator<(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:277
const_iterator_base(const pointer_and_size *ptrptr)
Definition: WorkStorage.hpp:253
std::random_access_iterator_tag iterator_category
Definition: WorkStorage.hpp:251
RAJA_HOST_DEVICE friend difference_type operator-(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:263
const typename WorkStorage::value_type value_type
Definition: WorkStorage.hpp:247
RAJA_HOST_DEVICE friend bool operator==(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:270
RAJA_HOST_DEVICE friend bool operator<(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:821
const_iterator_base(const char *array_pos, size_type stride)
Definition: WorkStorage.hpp:791
RAJA_HOST_DEVICE const_iterator_base & operator+=(difference_type n)
Definition: WorkStorage.hpp:801
RAJA_HOST_DEVICE friend difference_type operator-(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:807
RAJA_HOST_DEVICE friend bool operator==(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:814
typename WorkStorage::const_reference reference
Definition: WorkStorage.hpp:476
RAJA_HOST_DEVICE friend bool operator<(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:510
RAJA_HOST_DEVICE const_iterator_base & operator+=(difference_type n)
Definition: WorkStorage.hpp:490
typename WorkStorage::difference_type difference_type
Definition: WorkStorage.hpp:477
RAJA_HOST_DEVICE friend difference_type operator-(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:496
const_iterator_base(const char *array_begin, const size_type *offset_iter)
Definition: WorkStorage.hpp:480
RAJA_HOST_DEVICE friend bool operator==(const_iterator_base const &lhs_iter, const_iterator_base const &rhs_iter)
Definition: WorkStorage.hpp:503
const typename WorkStorage::value_type value_type
Definition: WorkStorage.hpp:474
Definition: WorkStruct.hpp:40
Definition: WorkStorage.hpp:52
std::random_access_iterator_tag iterator_category
Definition: WorkStorage.hpp:58
RAJA_HOST_DEVICE friend bool operator<=(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:175
RAJA_HOST_DEVICE random_access_iterator operator--(int)
Definition: WorkStorage.hpp:101
random_access_iterator(random_access_iterator const &)=default
RAJA_HOST_DEVICE friend random_access_iterator operator+(random_access_iterator const &lhs, difference_type rhs)
Definition: WorkStorage.hpp:120
RAJA_HOST_DEVICE friend bool operator==(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:154
typename base::pointer pointer
Definition: WorkStorage.hpp:55
RAJA_HOST_DEVICE random_access_iterator & operator+=(difference_type rhs)
Definition: WorkStorage.hpp:108
const typename base::value_type value_type
Definition: WorkStorage.hpp:54
RAJA_HOST_DEVICE pointer operator->() const
Definition: WorkStorage.hpp:73
RAJA_HOST_DEVICE friend random_access_iterator operator+(difference_type lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:129
RAJA_HOST_DEVICE friend difference_type operator-(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:147
RAJA_HOST_DEVICE random_access_iterator & operator--()
Definition: WorkStorage.hpp:95
RAJA_HOST_DEVICE random_access_iterator & operator-=(difference_type rhs)
Definition: WorkStorage.hpp:114
random_access_iterator(random_access_iterator &&)=default
RAJA_HOST_DEVICE friend bool operator>=(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:189
random_access_iterator & operator=(random_access_iterator &&)=default
RAJA_HOST_DEVICE random_access_iterator & operator++()
Definition: WorkStorage.hpp:82
random_access_iterator & operator=(random_access_iterator const &)=default
typename base::reference reference
Definition: WorkStorage.hpp:56
RAJA_HOST_DEVICE friend bool operator<(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:168
RAJA_HOST_DEVICE friend random_access_iterator operator-(random_access_iterator const &lhs, difference_type rhs)
Definition: WorkStorage.hpp:138
typename base::difference_type difference_type
Definition: WorkStorage.hpp:57
RAJA_HOST_DEVICE reference operator[](difference_type i) const
Definition: WorkStorage.hpp:75
RAJA_HOST_DEVICE friend bool operator>(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:182
RAJA_HOST_DEVICE random_access_iterator operator++(int)
Definition: WorkStorage.hpp:88
RAJA_HOST_DEVICE friend bool operator!=(random_access_iterator const &lhs, random_access_iterator const &rhs)
Definition: WorkStorage.hpp:161
iterator_base base
Definition: WorkStorage.hpp:53
RAJA_HOST_DEVICE reference operator*() const
Definition: WorkStorage.hpp:68
Definition: WorkGroup.hpp:59