Ginkgo  Generated from pipelines/1363093349 branch based on develop. Ginkgo version 1.9.0
A numerical linear algebra library targeting many-core architectures
array.hpp
1 // SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4 
5 #ifndef GKO_PUBLIC_CORE_BASE_ARRAY_HPP_
6 #define GKO_PUBLIC_CORE_BASE_ARRAY_HPP_
7 
8 
9 #include <algorithm>
10 #include <iterator>
11 #include <memory>
12 #include <type_traits>
13 #include <utility>
14 
15 #include <ginkgo/core/base/exception.hpp>
16 #include <ginkgo/core/base/exception_helpers.hpp>
17 #include <ginkgo/core/base/executor.hpp>
18 #include <ginkgo/core/base/types.hpp>
19 #include <ginkgo/core/base/utils.hpp>
20 
21 
22 namespace gko {
23 
24 
25 template <typename ValueType>
26 class array;
27 
28 
29 namespace detail {
30 
31 
38 template <typename SourceType, typename TargetType>
39 void convert_data(std::shared_ptr<const Executor> exec, size_type size,
40  const SourceType* src, TargetType* dst);
41 
42 
52 template <typename ValueType>
53 class const_array_view {
54 public:
58  using value_type = ValueType;
59 
67  const_array_view(std::shared_ptr<const Executor> exec, size_type size,
68  const ValueType* data)
69  : exec_{std::move(exec)}, size_{size}, data_{data}
70  {}
71 
72  /*
73  * To avoid any collisions with the value semantics of normal arrays,
74  * disable assignment and copy-construction altogether.
75  */
76  const_array_view& operator=(const const_array_view&) = delete;
77  const_array_view& operator=(const_array_view&&) = delete;
78  const_array_view(const const_array_view&) = delete;
79  /*
80  * TODO C++17: delete this overload as well, it is no longer necessary due
81  * to guaranteed RVO.
82  */
83  const_array_view(const_array_view&& other)
84  : const_array_view{other.exec_, other.size_, other.data_}
85  {
86  other.size_ = 0;
87  other.data_ = nullptr;
88  }
89 
95  size_type get_size() const noexcept { return size_; }
96 
102  GKO_DEPRECATED("use get_size() instead")
103  size_type get_num_elems() const noexcept { return get_size(); }
104 
110  const value_type* get_const_data() const noexcept { return data_; }
111 
117  std::shared_ptr<const Executor> get_executor() const noexcept
118  {
119  return exec_;
120  }
121 
125  bool is_owning() const noexcept { return false; }
126 
132  array<ValueType> copy_to_array() const;
133 
134 private:
135  std::shared_ptr<const Executor> exec_;
136  size_type size_;
137  const ValueType* data_;
138 };
139 
140 
141 template <typename ValueType>
142 using ConstArrayView GKO_DEPRECATED("please use const_array_view") =
143  const_array_view<ValueType>;
144 
145 
146 template <typename ValueType>
147 array<ValueType> array_const_cast(const_array_view<ValueType> view);
148 
149 
150 } // namespace detail
151 
152 
165 template <typename ValueType>
166 class array {
167 public:
171  using value_type = ValueType;
172 
177 
182 
196  array() noexcept
197  : size_(0), data_(nullptr, default_deleter{nullptr}), exec_(nullptr)
198  {}
199 
205  explicit array(std::shared_ptr<const Executor> exec) noexcept
206  : size_(0),
207  data_(nullptr, default_deleter{exec}),
208  exec_(std::move(exec))
209  {}
210 
218  array(std::shared_ptr<const Executor> exec, size_type size)
219  : size_(size),
220  data_(nullptr, default_deleter{exec}),
221  exec_(std::move(exec))
222  {
223  if (size > 0) {
224  data_.reset(exec_->alloc<value_type>(size));
225  }
226  }
227 
246  template <typename DeleterType>
247  array(std::shared_ptr<const Executor> exec, size_type size,
248  value_type* data, DeleterType deleter)
249  : size_{size}, data_(data, deleter), exec_{exec}
250  {}
251 
262  array(std::shared_ptr<const Executor> exec, size_type size,
263  value_type* data)
264  : array(exec, size, data, default_deleter{exec})
265  {}
266 
277  template <typename RandomAccessIterator>
278  array(std::shared_ptr<const Executor> exec, RandomAccessIterator begin,
279  RandomAccessIterator end)
280  : array(exec)
281  {
282  array tmp(exec->get_master(), std::distance(begin, end));
283  std::copy(begin, end, tmp.data_.get());
284  *this = std::move(tmp);
285  }
286 
297  template <typename T>
298  array(std::shared_ptr<const Executor> exec,
299  std::initializer_list<T> init_list)
300  : array(exec, begin(init_list), end(init_list))
301  {}
302 
312  array(std::shared_ptr<const Executor> exec, const array& other)
313  : array(exec)
314  {
315  *this = other;
316  }
317 
326  array(const array& other) : array(other.get_executor(), other) {}
327 
337  array(std::shared_ptr<const Executor> exec, array&& other) : array(exec)
338  {
339  *this = std::move(other);
340  }
341 
350  array(array&& other) : array(other.get_executor(), std::move(other)) {}
351 
365  static array view(std::shared_ptr<const Executor> exec, size_type size,
366  value_type* data)
367  {
368  return array{exec, size, data, view_deleter{}};
369  }
370 
384  static detail::const_array_view<ValueType> const_view(
385  std::shared_ptr<const Executor> exec, size_type size,
386  const value_type* data)
387  {
388  return {exec, size, data};
389  }
390 
396  {
397  return view(this->get_executor(), this->get_size(), this->get_data());
398  }
399 
404  detail::const_array_view<ValueType> as_const_view() const
405  {
406  return const_view(this->get_executor(), this->get_size(),
407  this->get_const_data());
408  }
409 
426  array& operator=(const array& other)
427  {
428  if (&other == this) {
429  return *this;
430  }
431  if (exec_ == nullptr) {
432  exec_ = other.get_executor();
433  data_ = data_manager{nullptr, other.data_.get_deleter()};
434  }
435  if (other.get_executor() == nullptr) {
436  this->clear();
437  return *this;
438  }
439 
440  if (this->is_owning()) {
441  this->resize_and_reset(other.get_size());
442  } else {
443  GKO_ENSURE_COMPATIBLE_BOUNDS(other.get_size(), this->get_size());
444  }
445  exec_->copy_from(other.get_executor(), other.get_size(),
446  other.get_const_data(), this->get_data());
447  return *this;
448  }
449 
480  {
481  if (&other == this) {
482  return *this;
483  }
484  if (exec_ == nullptr) {
485  exec_ = other.get_executor();
486  data_ = data_manager{nullptr, default_deleter{exec_}};
487  }
488  if (other.get_executor() == nullptr) {
489  this->clear();
490  return *this;
491  }
492  if (exec_ == other.get_executor()) {
493  // same device, only move the pointer
494  data_ = std::exchange(
495  other.data_, data_manager{nullptr, default_deleter{exec_}});
496  size_ = std::exchange(other.size_, 0);
497  } else {
498  // different device, copy the data
499  *this = other;
500  other.clear();
501  }
502  return *this;
503  }
504 
522  template <typename OtherValueType>
523  std::enable_if_t<!std::is_same<ValueType, OtherValueType>::value, array>&
525  {
526  if (this->exec_ == nullptr) {
527  this->exec_ = other.get_executor();
528  this->data_ = data_manager{nullptr, default_deleter{this->exec_}};
529  }
530  if (other.get_executor() == nullptr) {
531  this->clear();
532  return *this;
533  }
534 
535  if (this->is_owning()) {
536  this->resize_and_reset(other.get_size());
537  } else {
538  GKO_ENSURE_COMPATIBLE_BOUNDS(other.get_size(), this->get_size());
539  }
540  array<OtherValueType> tmp{this->exec_};
541  const OtherValueType* source = other.get_const_data();
542  // if we are on different executors: copy, then convert
543  if (this->exec_ != other.get_executor()) {
544  tmp = other;
545  source = tmp.get_const_data();
546  }
547  detail::convert_data(this->exec_, other.get_size(), source,
548  this->get_data());
549  return *this;
550  }
551 
569  array& operator=(const detail::const_array_view<ValueType>& other)
570  {
571  if (this->exec_ == nullptr) {
572  this->exec_ = other.get_executor();
573  this->data_ = data_manager{nullptr, default_deleter{this->exec_}};
574  }
575  if (other.get_executor() == nullptr) {
576  this->clear();
577  return *this;
578  }
579 
580  if (this->is_owning()) {
581  this->resize_and_reset(other.get_size());
582  } else {
583  GKO_ENSURE_COMPATIBLE_BOUNDS(other.get_size(), this->get_size());
584  }
585  array tmp{this->exec_};
586  const ValueType* source = other.get_const_data();
587  // if we are on different executors: copy
588  if (this->exec_ != other.get_executor()) {
589  tmp = other.copy_to_array();
590  source = tmp.get_const_data();
591  }
592  exec_->copy_from(other.get_executor(), other.get_size(), source,
593  this->get_data());
594  return *this;
595  }
596 
604  void clear() noexcept
605  {
606  size_ = 0;
607  data_.reset(nullptr);
608  }
609 
623  {
624  if (size == size_) {
625  return;
626  }
627  if (exec_ == nullptr) {
628  throw gko::NotSupported(__FILE__, __LINE__, __func__,
629  "gko::Executor (nullptr)");
630  }
631  if (!this->is_owning()) {
632  throw gko::NotSupported(__FILE__, __LINE__, __func__,
633  "Non owning gko::array cannot be resized.");
634  }
635 
636  if (size > 0 && this->is_owning()) {
637  size_ = size;
638  data_.reset(exec_->alloc<value_type>(size));
639  } else {
640  this->clear();
641  }
642  }
643 
649  void fill(const value_type value);
650 
656  size_type get_size() const noexcept { return size_; }
657 
663  GKO_DEPRECATED("use get_size() instead")
664  size_type get_num_elems() const noexcept { return get_size(); }
665 
673  value_type* get_data() noexcept { return data_.get(); }
674 
682  const value_type* get_const_data() const noexcept { return data_.get(); }
683 
689  std::shared_ptr<const Executor> get_executor() const noexcept
690  {
691  return exec_;
692  }
693 
700  void set_executor(std::shared_ptr<const Executor> exec)
701  {
702  if (exec == exec_) {
703  // moving to the same executor, no-op
704  return;
705  }
706  array tmp(std::move(exec));
707  tmp = *this;
708  exec_ = std::move(tmp.exec_);
709  data_ = std::move(tmp.data_);
710  }
711 
723  bool is_owning()
724  {
725  return data_.get_deleter().target_type() == typeid(default_deleter);
726  }
727 
728 
729 private:
730  // Allow other array types to access private members
731  template <typename OtherValueType>
732  friend class array;
733 
734  using data_manager =
735  std::unique_ptr<value_type[], std::function<void(value_type[])>>;
736 
737  size_type size_;
738  data_manager data_;
739  std::shared_ptr<const Executor> exec_;
740 };
741 
742 
743 template <typename ValueType>
744 using Array GKO_DEPRECATED("please use array") = array<ValueType>;
745 
746 
757 template <typename ValueType>
758 ValueType reduce_add(const array<ValueType>& input_arr,
759  const ValueType init_val = 0);
760 
771 template <typename ValueType>
772 void reduce_add(const array<ValueType>& input_arr, array<ValueType>& result);
773 
774 
786 template <typename ValueType>
787 array<ValueType> make_array_view(std::shared_ptr<const Executor> exec,
788  size_type size, ValueType* data)
789 {
790  return array<ValueType>::view(exec, size, data);
791 }
792 
793 
805 template <typename ValueType>
806 detail::const_array_view<ValueType> make_const_array_view(
807  std::shared_ptr<const Executor> exec, size_type size, const ValueType* data)
808 {
809  return array<ValueType>::const_view(exec, size, data);
810 }
811 
812 
813 namespace detail {
814 
815 
816 template <typename T>
817 struct temporary_clone_helper<array<T>> {
818  static std::unique_ptr<array<T>> create(
819  std::shared_ptr<const Executor> exec, array<T>* ptr, bool copy_data)
820  {
821  if (copy_data) {
822  return std::make_unique<array<T>>(std::move(exec), *ptr);
823  } else {
824  return std::make_unique<array<T>>(std::move(exec), ptr->get_size());
825  }
826  }
827 };
828 
829 template <typename T>
830 struct temporary_clone_helper<const array<T>> {
831  static std::unique_ptr<const array<T>> create(
832  std::shared_ptr<const Executor> exec, const array<T>* ptr, bool)
833  {
834  return std::make_unique<const array<T>>(std::move(exec), *ptr);
835  }
836 };
837 
838 
839 // specialization for non-constant arrays, copying back via assignment
840 template <typename T>
841 class copy_back_deleter<array<T>>
842  : public copy_back_deleter_from_assignment<array<T>> {
843 public:
844  using copy_back_deleter_from_assignment<
845  array<T>>::copy_back_deleter_from_assignment;
846 };
847 
848 
861 template <typename ValueType>
862 array<ValueType> array_const_cast(const_array_view<ValueType> view)
863 {
864  return array<ValueType>::view(
865  view.get_executor(), view.get_size(),
866  const_cast<ValueType*>(view.get_const_data()));
867 }
868 
869 
870 template <typename ValueType>
871 array<ValueType> const_array_view<ValueType>::copy_to_array() const
872 {
873  array<ValueType> result(this->get_executor(), this->get_size());
874  result.get_executor()->copy_from(this->get_executor(), this->get_size(),
875  this->get_const_data(), result.get_data());
876  return result;
877 }
878 
879 
880 } // namespace detail
881 } // namespace gko
882 
883 
884 #endif // GKO_PUBLIC_CORE_BASE_ARRAY_HPP_
gko::array::array
array(const array &other)
Creates a copy of another array.
Definition: array.hpp:326
gko::array::array
array(std::shared_ptr< const Executor > exec, array &&other)
Moves another array to a different executor.
Definition: array.hpp:337
gko::array::set_executor
void set_executor(std::shared_ptr< const Executor > exec)
Changes the Executor of the array, moving the allocated data to the new Executor.
Definition: array.hpp:700
gko::executor_deleter
This is a deleter that uses an executor's free method to deallocate the data.
Definition: executor.hpp:1173
gko::array::array
array(std::shared_ptr< const Executor > exec, RandomAccessIterator begin, RandomAccessIterator end)
Creates an array on the specified Executor and initializes it with values.
Definition: array.hpp:278
gko::layout_type::array
The matrix should be written as dense matrix in column-major order.
gko::array::is_owning
bool is_owning()
Tells whether this array owns its data or not.
Definition: array.hpp:723
gko::array::view
static array view(std::shared_ptr< const Executor > exec, size_type size, value_type *data)
Creates an array from existing memory.
Definition: array.hpp:365
gko::array::operator=
array & operator=(const detail::const_array_view< ValueType > &other)
Copies data from a const_array_view.
Definition: array.hpp:569
gko::null_deleter
This is a deleter that does not delete the object.
Definition: utils_helper.hpp:466
gko::array::operator=
array & operator=(array &&other)
Moves data from another array or view.
Definition: array.hpp:479
gko::array::array
array(std::shared_ptr< const Executor > exec, size_type size)
Creates an array on the specified Executor.
Definition: array.hpp:218
gko::size_type
std::size_t size_type
Integral type used for allocation quantities.
Definition: types.hpp:108
gko::array::array
array(std::shared_ptr< const Executor > exec, size_type size, value_type *data, DeleterType deleter)
Creates an array from existing memory.
Definition: array.hpp:247
gko::make_const_array_view
detail::const_array_view< ValueType > make_const_array_view(std::shared_ptr< const Executor > exec, size_type size, const ValueType *data)
Helper function to create a const array view deducing the value type.
Definition: array.hpp:806
gko::NotSupported
NotSupported is thrown in case it is not possible to perform the requested operation on the given obj...
Definition: exception.hpp:127
gko
The Ginkgo namespace.
Definition: abstract_factory.hpp:19
gko::array::array
array(std::shared_ptr< const Executor > exec, const array &other)
Creates a copy of another array on a different executor.
Definition: array.hpp:312
gko::array
An array is a container which encapsulates fixed-sized arrays, stored on the Executor tied to the arr...
Definition: array.hpp:26
gko::array::resize_and_reset
void resize_and_reset(size_type size)
Resizes the array so it is able to hold the specified number of elements.
Definition: array.hpp:622
gko::array::as_view
array< ValueType > as_view()
Returns a non-owning view of the memory owned by this array.
Definition: array.hpp:395
gko::array::get_data
value_type * get_data() noexcept
Returns a pointer to the block of memory used to store the elements of the array.
Definition: array.hpp:673
gko::array::operator=
std::enable_if_t<!std::is_same< ValueType, OtherValueType >::value, array > & operator=(const array< OtherValueType > &other)
Copies and converts data from another array with another data type.
Definition: array.hpp:524
gko::array::array
array() noexcept
Creates an empty array not tied to any executor.
Definition: array.hpp:196
gko::array::get_executor
std::shared_ptr< const Executor > get_executor() const noexcept
Returns the Executor associated with the array.
Definition: array.hpp:689
gko::array::operator=
array & operator=(const array &other)
Copies data from another array or view.
Definition: array.hpp:426
gko::make_array_view
array< ValueType > make_array_view(std::shared_ptr< const Executor > exec, size_type size, ValueType *data)
Helper function to create an array view deducing the value type.
Definition: array.hpp:787
gko::array::array
array(std::shared_ptr< const Executor > exec, std::initializer_list< T > init_list)
Creates an array on the specified Executor and initializes it with values.
Definition: array.hpp:298
gko::array::array
array(std::shared_ptr< const Executor > exec) noexcept
Creates an empty array tied to the specified Executor.
Definition: array.hpp:205
gko::array::array
array(array &&other)
Moves another array.
Definition: array.hpp:350
gko::array::clear
void clear() noexcept
Deallocates all data used by the array.
Definition: array.hpp:604
gko::array::const_view
static detail::const_array_view< ValueType > const_view(std::shared_ptr< const Executor > exec, size_type size, const value_type *data)
Creates a constant (immutable) array from existing memory.
Definition: array.hpp:384
gko::array::get_const_data
const value_type * get_const_data() const noexcept
Returns a constant pointer to the block of memory used to store the elements of the array.
Definition: array.hpp:682
gko::array::as_const_view
detail::const_array_view< ValueType > as_const_view() const
Returns a non-owning constant view of the memory owned by this array.
Definition: array.hpp:404
gko::array::array
array(std::shared_ptr< const Executor > exec, size_type size, value_type *data)
Creates an array from existing memory.
Definition: array.hpp:262
gko::array::value_type
ValueType value_type
The type of elements stored in the array.
Definition: array.hpp:171
gko::array::get_size
size_type get_size() const noexcept
Returns the number of elements in the array.
Definition: array.hpp:656