ryujin 2.1.1 revision ee5cbcbf2346c1299c942d0e1f13b46449973c18
Loading...
Searching...
No Matches
gpu.h
Go to the documentation of this file.
1//
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3// Copyright (C) 2026 by the ryujin authors
4//
5
6#pragma once
7
8#include <compile_time_options.h>
10#include <simd.h>
11
12#include <deal.II/base/exceptions.h>
13#include <deal.II/base/memory_space.h>
14#include <deal.II/base/vectorization.h>
15
16#include <string>
17#include <type_traits>
18
19namespace ryujin
20{
29 inline constexpr bool have_separate_memory_spaces =
30 !std::is_same_v<dealii::MemorySpace::Host::kokkos_space,
31 dealii::MemorySpace::Default::kokkos_space>;
32
33
46 inline constexpr unsigned int warp_size =
48 : dealii::VectorizedArray<NUMBER>::size();
49
50
63 std::conditional_t<have_separate_memory_spaces,
64 dealii::MemorySpace::Default,
65 dealii::MemorySpace::Host>;
66
67
75 template <typename MemorySpace>
77 std::conditional_t<std::is_same_v<MemorySpace, dealii::MemorySpace::Host>,
78 dealii::MemorySpace::Default,
79 dealii::MemorySpace::Host>;
80
81
130
131
145
146
193 template <typename Derived>
195 {
196 public:
201
209 template <typename MemorySpace>
210 bool is_resident() const;
211
222 template <typename MemorySpace>
223 bool is_pinned() const;
224
233 template <typename MemorySpace>
235
247 template <typename MemorySpace>
249
254
263
264 protected:
266
270
271 MirroredStorage() = default;
272
282 template <typename MemorySpace>
284
296 template <typename MemorySpace>
298
308 void reset_residency(const bool host_resident, const bool default_resident);
309
311
312 private:
317
318 using HostSpace = dealii::MemorySpace::Host;
319 using DefaultSpace = dealii::MemorySpace::Default;
320
321 template <typename MemorySpace>
322 bool &residency_flag() const;
323
324 Derived &derived();
325 const Derived &derived() const;
326
327 mutable bool host_resident_ = false;
328 mutable bool default_resident_ = false;
329
331
333 };
334
335
458 template <typename T>
459 class Mirrored : public MirroredStorage<Mirrored<T>>
460 {
461 public:
467 static constexpr bool is_array = std::is_pointer_v<T>;
468
473 using value_type = std::remove_pointer_t<T>;
474
475 static_assert(std::is_trivially_copyable_v<value_type>,
476 "The stored type has to be trivially copyable so that we "
477 "can move it into device memory");
478
479 static_assert(!std::is_pointer_v<value_type>,
480 "Only a single level of indirection is supported: a "
481 "Mirrored<T *> object maintains a one dimensional array of "
482 "objects of type T");
483
484 static_assert(!std::is_array_v<T>,
485 "An array of objects is spelled Mirrored<T *>, and not "
486 "Mirrored<T[]>");
487
499 template <typename InitializedMemorySpace = dealii::MemorySpace::Host>
500 Mirrored(const std::string &label = "mirrored object",
503 InitializedMemorySpace = {})
504 requires(!is_array);
505
517 template <typename InitializedMemorySpace = dealii::MemorySpace::Host>
518 Mirrored(const std::string &label = "mirrored array",
519 const std::size_t size = 0,
522 InitializedMemorySpace = {})
523 requires(is_array);
524
530 template <typename InitializedMemorySpace = dealii::MemorySpace::Host>
531 void reinit(const std::size_t size,
534 InitializedMemorySpace = {})
535 requires(is_array);
536
540 std::size_t size() const
541 requires(is_array);
542
548 template <typename MemorySpace = dealii::MemorySpace::Host>
550
556 template <typename MemorySpace = dealii::MemorySpace::Host>
557 const value_type *view() const;
558
559 private:
564
565 using HostSpace = dealii::MemorySpace::Host;
566 using DefaultSpace = dealii::MemorySpace::Default;
567
568 template <typename MemorySpace>
569 using KokkosView = Kokkos::View<T, typename MemorySpace::kokkos_space>;
570
574 template <typename MemorySpace>
575 KokkosView<MemorySpace> &storage() const;
576
582 template <typename InitializedMemorySpace>
583 void initialize_storage();
584
585 /*
586 * Storage primitives required by the MirroredStorage base class:
587 */
588
589 template <typename MemorySpace>
590 void allocate_storage() const;
591
592 template <typename To, typename From>
593 void deep_copy_storage() const;
594
595 template <typename MemorySpace>
596 void deallocate_storage();
597
598 std::string label_;
599
600 /* The number of stored objects, only used in the array mode: */
601 std::size_t size_ = 0;
602
603 mutable KokkosView<HostSpace> host_;
604 mutable KokkosView<DefaultSpace> default_;
605
606 friend class MirroredStorage<Mirrored<T>>;
607
609 };
610
611
639 template <int dim, typename Number, typename MemorySpace, typename Object>
641 {
642 private:
643 /* Do not instantiate VectorizedArray for device code */
644 using SimdNumber = std::conditional_t<
645 std::is_same_v<MemorySpace, dealii::MemorySpace::Host>,
646 dealii::VectorizedArray<Number>,
647 Number>;
648
649 /*
650 * FIXME: a little dance around calling the templated view<...>()
651 * operator in the equation classes. We do this to support equations
652 * that have not yet been ported and do not support MemorySpace.
653 */
654 template <typename T>
655 static auto create_view(const Object &object)
656 {
657 if constexpr (std::is_same_v<MemorySpace, dealii::MemorySpace::Host>)
658 return object.template view<dim, T>();
659 else
660 return object.template view<dim, T, MemorySpace>();
661 }
662
663 using ScalarView =
664 decltype(create_view<Number>(std::declval<const Object &>()));
665 using SimdView =
666 decltype(create_view<SimdNumber>(std::declval<const Object &>()));
667
668 public:
669 SelectView(const Object &object)
670 : scalar_view_(create_view<Number>(object))
671 , simd_view_(create_view<SimdNumber>(object))
672 {
673 }
674
679 template <typename T>
680 DEAL_II_HOST_DEVICE_ALWAYS_INLINE const auto &view() const
681 {
682 if constexpr (std::is_same_v<T, typename get_value_type<T>::type>)
683 return scalar_view_;
684 else
685 return simd_view_;
686 }
687
688 private:
689 ScalarView scalar_view_;
690 SimdView simd_view_;
691 };
692
693
699 template <int dim, typename Number, typename MemorySpace, typename Object>
700 auto make_select_view(const Object &object)
701 {
703 }
704
705
706#ifndef DOXYGEN
707 /*
708 * -------------------------------------------------------------------------
709 * Inline function definitions
710 * -------------------------------------------------------------------------
711 */
712
713
714 template <typename Derived>
715 template <typename MemorySpace>
716 inline bool MirroredStorage<Derived>::is_resident() const
717 {
718 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
719 std::is_same_v<MemorySpace, DefaultSpace>,
720 "Unexpected memory space");
721
722 if constexpr (!have_separate_memory_spaces) {
723 Assert(host_resident_ == default_resident_,
724 dealii::ExcMessage(
725 "Internal error: the host and default memory spaces coincide "
726 "but the two residency flags do not. There is only a single "
727 "allocation, so the object has to be resident on both memory "
728 "spaces, or on neither of them."));
729 return host_resident_ || default_resident_;
730 }
731
732 return residency_flag<MemorySpace>();
733 }
734
735
736 template <typename Derived>
737 template <typename MemorySpace>
738 inline bool MirroredStorage<Derived>::is_pinned() const
739 {
740 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
741 std::is_same_v<MemorySpace, DefaultSpace>,
742 "Unexpected memory space");
743
744 if constexpr (!have_separate_memory_spaces) {
745 return false;
746 }
747
748 if constexpr (std::is_same_v<MemorySpace, HostSpace>) {
749 return transfer_policy_ ==
751
752 } else {
753 return transfer_policy_ ==
755 }
756 }
757
758
759 template <typename Derived>
760 template <typename MemorySpace>
762 {
763 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
764 std::is_same_v<MemorySpace, DefaultSpace>,
765 "Unexpected memory space");
766
767 if constexpr (!have_separate_memory_spaces) {
768 /*
769 * If both memory spaces coincide there is only a single allocation
770 * that is resident on both of them: there is nothing to copy.
771 */
772 Assert(host_resident_ && default_resident_,
773 dealii::ExcMessage(
774 "Unable to copy to the chosen memory space: the host and "
775 "default memory spaces coincide but the object is not "
776 "resident on them. The object has not been properly "
777 "initialized."));
778 return;
779 }
780
781 Assert(!is_pinned<MemorySpace>() || is_resident<MemorySpace>(),
782 dealii::ExcMessage(
783 "Unable to copy to the chosen memory space: the selected "
784 "transfer policy pins the chosen memory space but the object "
785 "is not resident on it. The object has not been properly "
786 "initialized."));
787
788 if (is_resident<MemorySpace>())
789 return;
790
791 using OtherSpace = other_space_t<MemorySpace>;
792 Assert(is_resident<OtherSpace>(),
793 dealii::ExcMessage(
794 "Unable to copy to the chosen memory space: the object has "
795 "not been properly initialized."));
796
797 derived().template allocate_storage<MemorySpace>();
798 derived().template deep_copy_storage<MemorySpace, OtherSpace>();
799 residency_flag<MemorySpace>() = true;
800 }
801
802
803 template <typename Derived>
804 template <typename MemorySpace>
806 {
807 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
808 std::is_same_v<MemorySpace, DefaultSpace>,
809 "Unexpected memory space");
810
811 if constexpr (!have_separate_memory_spaces) {
812 /*
813 * If both memory spaces coincide there is only a single allocation
814 * that no memory transfer can ever deallocate: there is nothing to
815 * move.
816 */
817 Assert(host_resident_ && default_resident_,
818 dealii::ExcMessage(
819 "Unable to move to the chosen memory space: the host and "
820 "default memory spaces coincide but the object is not "
821 "resident on them. The object has not been properly "
822 "initialized."));
823 return;
824 }
825
826 Assert(!is_pinned<MemorySpace>() || is_resident<MemorySpace>(),
827 dealii::ExcMessage(
828 "Unable to move to the chosen memory space: the selected "
829 "transfer policy pins the chosen memory space but the object "
830 "is not resident on it. The object has not been properly "
831 "initialized."));
832
833 using OtherSpace = other_space_t<MemorySpace>;
834
835 Assert(!is_pinned<OtherSpace>(),
836 dealii::ExcMessage(
837 "Unable to move to the chosen memory space: the selected "
838 "transfer policy requires the data to remain resident on "
839 "the other memory space."));
840
841 if (!is_resident<MemorySpace>()) {
842 Assert(is_resident<OtherSpace>(),
843 dealii::ExcMessage(
844 "Unable to move to the chosen memory space: the object has "
845 "not been properly initialized."));
846
847 derived().template allocate_storage<MemorySpace>();
848 derived().template deep_copy_storage<MemorySpace, OtherSpace>();
849 residency_flag<MemorySpace>() = true;
850 }
851
852 if (residency_flag<OtherSpace>()) {
853 derived().template deallocate_storage<OtherSpace>();
854 residency_flag<OtherSpace>() = false;
855 }
856 }
857
858
859 template <typename Derived>
861 {
862 return transfer_policy_;
863 }
864
865
866 template <typename Derived>
869 {
870 transfer_policy_ = transfer_policy;
871
872 Assert(!is_pinned<HostSpace>() || is_resident<HostSpace>(),
873 dealii::ExcMessage(
874 "Unable to select the given transfer policy: the policy pins "
875 "the host memory space but the object is not resident on it. "
876 "Transfer the data to the pinned memory space before selecting "
877 "the transfer policy."));
878
879 Assert(!is_pinned<DefaultSpace>() || is_resident<DefaultSpace>(),
880 dealii::ExcMessage(
881 "Unable to select the given transfer policy: the policy pins "
882 "the default memory space but the object is not resident on "
883 "it. Transfer the data to the pinned memory space before "
884 "selecting the transfer policy."));
885 }
886
887
888 template <typename Derived>
889 template <typename MemorySpace>
891 {
892 if (performs_implicit_transfers(transfer_policy_)) {
893 copy_to_memory_space<MemorySpace>();
894
895 } else {
896 Assert(is_resident<MemorySpace>(),
897 dealii::ExcMessage(
898 "The chosen memory space is not resident. Either call "
899 "copy_to_memory_space() / move_to_memory_space() prior to "
900 "requesting a view, or select one of the "
901 "TransferPolicy::implicit_transfers* policies."));
902 }
903 }
904
905
906 template <typename Derived>
907 template <typename MemorySpace>
909 {
910 if (performs_implicit_transfers(transfer_policy_)) {
911 Assert(!is_pinned<other_space_t<MemorySpace>>(),
912 dealii::ExcMessage(
913 "Unable to request a writable view on the chosen memory "
914 "space: the selected transfer policy requires the data to "
915 "remain resident on the other memory space."));
916
917 move_to_memory_space<MemorySpace>();
918
919 } else {
920 Assert(is_resident<MemorySpace>(),
921 dealii::ExcMessage(
922 "The chosen memory space is not resident. Either call "
923 "copy_to_memory_space() / move_to_memory_space() prior to "
924 "requesting a view, or select one of the "
925 "TransferPolicy::implicit_transfers* policies."));
926 }
927 }
928
929
930 template <typename Derived>
931 inline void
932 MirroredStorage<Derived>::reset_residency(const bool host_resident,
933 const bool default_resident)
934 {
935 if constexpr (!have_separate_memory_spaces) {
936 Assert(host_resident == default_resident,
937 dealii::ExcMessage(
938 "Unable to reset the residency flags: the host and default "
939 "memory spaces coincide, so there is only a single "
940 "allocation and both flags have to coincide as well."));
941 }
942
943 host_resident_ = host_resident;
944 default_resident_ = default_resident;
945
946 Assert(!is_pinned<HostSpace>() || is_resident<HostSpace>(),
947 dealii::ExcMessage(
948 "Unable to reset the residency flags: the selected transfer "
949 "policy pins the host memory space and requires the data to "
950 "remain resident on it."));
951
952 Assert(!is_pinned<DefaultSpace>() || is_resident<DefaultSpace>(),
953 dealii::ExcMessage(
954 "Unable to reset the residency flags: the selected transfer "
955 "policy pins the default memory space and requires the data "
956 "to remain resident on it."));
957 }
958
959
960 template <typename Derived>
961 template <typename MemorySpace>
962 inline bool &MirroredStorage<Derived>::residency_flag() const
963 {
964 if constexpr (std::is_same_v<MemorySpace, HostSpace>)
965 return host_resident_;
966 else
967 return default_resident_;
968 }
969
970
971 template <typename Derived>
972 inline Derived &MirroredStorage<Derived>::derived()
973 {
974 return static_cast<Derived &>(*this);
975 }
976
977
978 template <typename Derived>
979 inline const Derived &MirroredStorage<Derived>::derived() const
980 {
981 return static_cast<const Derived &>(*this);
982 }
983
984
985 template <typename T>
986 template <typename InitializedMemorySpace>
987 Mirrored<T>::Mirrored(const std::string &label,
989 InitializedMemorySpace)
990 requires(!is_array)
991 : label_(label)
992 {
993 initialize_storage<InitializedMemorySpace>();
994
995 /* The transfer policy is selected last, see MirroredStorage: */
996 this->set_transfer_policy(transfer_policy);
997 }
998
999
1000 template <typename T>
1001 template <typename InitializedMemorySpace>
1002 Mirrored<T>::Mirrored(const std::string &label,
1003 const std::size_t size,
1005 InitializedMemorySpace)
1006 requires(is_array)
1007 : label_(label)
1008 {
1009 reinit(size, transfer_policy, InitializedMemorySpace{});
1010 }
1011
1012
1013 template <typename T>
1014 template <typename InitializedMemorySpace>
1015 void Mirrored<T>::reinit(const std::size_t size,
1017 InitializedMemorySpace)
1018 requires(is_array)
1019 {
1020 /*
1021 * Drop the transfer policy for the duration of the reinit so that a
1022 * pinned memory space of a previous policy does not interfere:
1023 */
1025
1026 size_ = size;
1027
1028 initialize_storage<InitializedMemorySpace>();
1029
1030 /* The transfer policy is selected last, see MirroredStorage: */
1031 this->set_transfer_policy(transfer_policy);
1032 }
1033
1034
1035 template <typename T>
1036 inline std::size_t Mirrored<T>::size() const
1037 requires(is_array)
1038 {
1039 return size_;
1040 }
1041
1042
1043 template <typename T>
1044 template <typename InitializedMemorySpace>
1045 void Mirrored<T>::initialize_storage()
1046 {
1047 static_assert(std::is_same_v<InitializedMemorySpace, HostSpace> ||
1048 std::is_same_v<InitializedMemorySpace, DefaultSpace>,
1049 "Unexpected memory space");
1050
1051 /*
1052 * Note: If the host and default memory spaces coincide then only the
1053 * host storage is ever allocated.
1054 */
1055 if constexpr (have_separate_memory_spaces &&
1056 !std::is_same_v<InitializedMemorySpace, HostSpace>) {
1057 /* Drop possibly stale storage from a previous reinit(): */
1058 deallocate_storage<HostSpace>();
1059 allocate_storage<DefaultSpace>();
1060
1061 this->reset_residency(/*host*/ false, /*default*/ true);
1062
1063 } else {
1064 if constexpr (have_separate_memory_spaces)
1065 deallocate_storage<DefaultSpace>();
1066 allocate_storage<HostSpace>();
1067
1068 /*
1069 * If both memory spaces coincide the single host allocation is
1070 * trivially resident on both of them:
1071 */
1072 this->reset_residency(/*host*/ true,
1073 /*default*/ !have_separate_memory_spaces);
1074 }
1075 }
1076
1077
1078 template <typename T>
1079 template <typename MemorySpace>
1080 inline auto Mirrored<T>::view() -> value_type *
1081 {
1082 this->template prepare_write_access<MemorySpace>();
1083
1084 return storage<MemorySpace>().data();
1085 }
1086
1087
1088 template <typename T>
1089 template <typename MemorySpace>
1090 inline auto Mirrored<T>::view() const -> const value_type *
1091 {
1092 this->template prepare_read_access<MemorySpace>();
1093
1094 return storage<MemorySpace>().data();
1095 }
1096
1097
1098 template <typename T>
1099 template <typename MemorySpace>
1100 inline auto Mirrored<T>::storage() const -> KokkosView<MemorySpace> &
1101 {
1102 static_assert(std::is_same_v<MemorySpace, HostSpace> ||
1103 std::is_same_v<MemorySpace, DefaultSpace>,
1104 "Unexpected memory space");
1105
1106 /*
1107 * Note: If the host and default memory spaces coincide then only the
1108 * host storage is ever allocated.
1109 */
1110 if constexpr (have_separate_memory_spaces &&
1111 !std::is_same_v<MemorySpace, HostSpace>)
1112 return default_;
1113 else
1114 return host_;
1115 }
1116
1117
1118 template <typename T>
1119 template <typename MemorySpace>
1120 inline void Mirrored<T>::allocate_storage() const
1121 {
1122 if constexpr (is_array)
1123 storage<MemorySpace>() = KokkosView<MemorySpace>(label_, size_);
1124 else
1125 storage<MemorySpace>() = KokkosView<MemorySpace>(label_);
1126 }
1127
1128
1129 template <typename T>
1130 template <typename To, typename From>
1131 inline void Mirrored<T>::deep_copy_storage() const
1132 {
1133 Kokkos::deep_copy(/*dst*/ storage<To>(), /*src*/ storage<From>());
1134 }
1135
1136
1137 template <typename T>
1138 template <typename MemorySpace>
1139 inline void Mirrored<T>::deallocate_storage()
1140 {
1141 storage<MemorySpace>() = KokkosView<MemorySpace>();
1142 }
1143
1144
1145#endif
1146} // namespace ryujin
void set_transfer_policy(const TransferPolicy transfer_policy)
void copy_to_memory_space() const
void reset_residency(const bool host_resident, const bool default_resident)
TransferPolicy transfer_policy() const
void prepare_read_access() const
bool is_resident() const
std::size_t size() const
static constexpr bool is_array
Definition gpu.h:467
std::remove_pointer_t< T > value_type
Definition gpu.h:473
value_type * view()
Mirrored(const std::string &label="mirrored array", const std::size_t size=0, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers, InitializedMemorySpace={})
Mirrored(const std::string &label="mirrored object", const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers, InitializedMemorySpace={})
void reinit(const std::size_t size, const TransferPolicy transfer_policy=TransferPolicy::explicit_transfers, InitializedMemorySpace={})
DEAL_II_HOST_DEVICE_ALWAYS_INLINE const auto & view() const
Definition gpu.h:680
SelectView(const Object &object)
Definition gpu.h:669
auto make_select_view(const Object &object)
Definition gpu.h:700
std::conditional_t< std::is_same_v< MemorySpace, dealii::MemorySpace::Host >, dealii::MemorySpace::Default, dealii::MemorySpace::Host > other_space_t
Definition gpu.h:79
constexpr unsigned int warp_size
Definition gpu.h:46
std::conditional_t< have_separate_memory_spaces, dealii::MemorySpace::Default, dealii::MemorySpace::Host > selected_memory_space_t
Definition gpu.h:65
TransferPolicy
Definition gpu.h:88
constexpr bool performs_implicit_transfers(const TransferPolicy policy)
Definition gpu.h:139
constexpr bool have_separate_memory_spaces
Definition gpu.h:29