xref: /petsc/src/sys/objects/device/impls/impldevicebase.hpp (revision d5b43468fb8780a8feea140ccd6fa3e6a50411cc)
1 #ifndef IMPLDEVICEBASE_HPP
2 #define IMPLDEVICEBASE_HPP
3 
4 #if defined(__cplusplus)
5   #include <petsc/private/deviceimpl.h>
6   #include <petsc/private/viewerimpl.h>
7 
8   #include <petsc/private/cpp/crtp.hpp>
9   #include <petsc/private/cpp/type_traits.hpp>
10   #include <petsc/private/cpp/utility.hpp>
11   #include <petsc/private/cpp/array.hpp>
12 
13   #include <cstring> // for std::strlen
14 
15 namespace Petsc
16 {
17 
18 namespace device
19 {
20 
21 namespace impl
22 {
23 
24 template <typename Derived> // CRTP
25 class DeviceBase : public util::crtp<Derived, DeviceBase> {
26 public:
27   using derived_type            = Derived;
28   using createContextFunction_t = PetscErrorCode (*)(PetscDeviceContext);
29 
30   // default constructor
31   constexpr DeviceBase(createContextFunction_t f) noexcept : create_(f) { }
32 
33   template <typename T = derived_type>
34   PETSC_NODISCARD static constexpr PetscDeviceType PETSC_DEVICE_IMPL() noexcept
35   {
36     return T::PETSC_DEVICE_IMPL_();
37   }
38 
39   PETSC_NODISCARD PetscErrorCode        getDevice(PetscDevice, PetscInt) noexcept;
40   PETSC_NODISCARD static PetscErrorCode configureDevice(PetscDevice) noexcept;
41   PETSC_NODISCARD static PetscErrorCode viewDevice(PetscDevice, PetscViewer) noexcept;
42   PETSC_NODISCARD static PetscErrorCode getAttribute(PetscDevice, PetscDeviceAttribute, void *) noexcept;
43 
44 protected:
45   // function to create a PetscDeviceContext (the (*create) function pointer usually set
46   // via XXXSetType() for other PETSc objects)
47   const createContextFunction_t create_;
48 
49   // if you want the base class to handle the entire options query, has the same arguments as
50   // PetscOptionDeviceBasic
51   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceAll(MPI_Comm, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept;
52 
53   // if you want to start and end the options query yourself, but still want all the default
54   // options
55   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceBasic(PetscOptionItems *, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept;
56 
57   // option templates to follow, each one has two forms:
58   // - A simple form returning only the value and flag. This gives no control over the message,
59   //   arguments to the options query or otherwise
60   // - A complex form, which allows you to pass most of the options query arguments *EXCEPT*
61   //   - The options query function called
62   //   - The option string
63 
64   // option template for initializing the device
65   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, PetscDeviceInitType *, PetscBool *) noexcept;
66   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
67   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, T &&...) noexcept;
68   // option template for selecting the default device
69   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, PetscInt *, PetscBool *) noexcept;
70   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
71   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, T &&...) noexcept;
72   // option templates for viewing a device
73   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, PetscBool *, PetscBool *) noexcept;
74   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
75   PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, T &&...) noexcept;
76 
77 private:
78   // base function for all options templates above, they basically just reformat the arguments,
79   // create the option string and pass it off to this function
80   template <typename... T, typename F = PetscErrorCode (*)(PetscOptionItems *, const char *, T &&...)>
81   PETSC_NODISCARD static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems *, const char[], T &&...) noexcept;
82 
83   // default crtp implementations
84   PETSC_NODISCARD static PetscErrorCode init_device_id_(PetscInt *id) noexcept
85   {
86     PetscFunctionBegin;
87     *id = 0;
88     PetscFunctionReturn(0);
89   }
90 
91   PETSC_NODISCARD static constexpr PetscErrorCode configure_device_(PetscDevice) noexcept { return 0; }
92   PETSC_NODISCARD static constexpr PetscErrorCode view_device_(PetscDevice, PetscViewer) noexcept { return 0; }
93 };
94 
95 template <typename D>
96 inline PetscErrorCode DeviceBase<D>::getDevice(PetscDevice device, PetscInt id) noexcept
97 {
98   PetscFunctionBegin;
99   PetscCall(this->underlying().init_device_id_(&id));
100   device->deviceId           = id;
101   device->ops->createcontext = this->underlying().create_;
102   device->ops->configure     = this->underlying().configureDevice;
103   device->ops->view          = this->underlying().viewDevice;
104   device->ops->getattribute  = this->underlying().getAttribute;
105   PetscFunctionReturn(0);
106 }
107 
108 template <typename D>
109 inline PetscErrorCode DeviceBase<D>::configureDevice(PetscDevice device) noexcept
110 {
111   PetscFunctionBegin;
112   PetscCall(derived_type::configure_device_(device));
113   PetscFunctionReturn(0);
114 }
115 
116 template <typename D>
117 inline PetscErrorCode DeviceBase<D>::viewDevice(PetscDevice device, PetscViewer viewer) noexcept
118 {
119   PetscFunctionBegin;
120   PetscCall(derived_type::view_device_(device, viewer));
121   PetscFunctionReturn(0);
122 }
123 
124 template <typename D>
125 inline PetscErrorCode DeviceBase<D>::getAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) noexcept
126 {
127   PetscFunctionBegin;
128   PetscCall(derived_type::get_attribute_(device->deviceId, attr, value));
129   PetscFunctionReturn(0);
130 }
131 
132 template <typename D>
133 template <typename... T, typename F>
134 inline PetscErrorCode DeviceBase<D>::PetscOptionDevice(F &&OptionsFunction, PetscOptionItems *PetscOptionsObject, const char optstub[], T &&...args) noexcept
135 {
136   constexpr auto dtype    = PETSC_DEVICE_IMPL();
137   const auto     implname = PetscDeviceTypes[dtype];
138   auto           buf      = std::array<char, 128>{};
139   constexpr auto buflen   = buf.size() - 1;
140 
141   PetscFunctionBegin;
142   if (PetscDefined(USE_DEBUG)) {
143     const auto len = std::strlen(optstub) + std::strlen(implname);
144 
145     PetscCheck(len < buflen, PetscOptionsObject->comm, PETSC_ERR_PLIB, "char buffer is not large enough to hold '%s%s'; have %zu need %zu", optstub, implname, buflen, len);
146   }
147   PetscCall(PetscSNPrintf(buf.data(), buflen, "%s%s", optstub, implname));
148   PetscCall(OptionsFunction(PetscOptionsObject, buf.data(), std::forward<T>(args)...));
149   PetscFunctionReturn(0);
150 }
151 
152 template <typename D>
153 template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
154 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept
155 {
156   PetscFunctionBegin;
157   PetscCall(PetscOptionDevice(PetscOptionsEList_Private, PetscOptionsObject, "-device_enable_", std::forward<T>(args)...));
158   PetscFunctionReturn(0);
159 }
160 
161 template <typename D>
162 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems *PetscOptionsObject, PetscDeviceInitType *inittype, PetscBool *flag) noexcept
163 {
164   auto type = static_cast<PetscInt>(util::integral_value(*inittype));
165 
166   PetscFunctionBegin;
167   PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, "How (or whether) to initialize a device", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[type], &type, flag));
168   *inittype = static_cast<PetscDeviceInitType>(type);
169   PetscFunctionReturn(0);
170 }
171 
172 template <typename D>
173 template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
174 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept
175 {
176   PetscFunctionBegin;
177   PetscCall(PetscOptionDevice(PetscOptionsInt_Private, PetscOptionsObject, "-device_select_", std::forward<T>(args)...));
178   PetscFunctionReturn(0);
179 }
180 
181 template <typename D>
182 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems *PetscOptionsObject, PetscInt *id, PetscBool *flag) noexcept
183 {
184   PetscFunctionBegin;
185   PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, "Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-" PetscStringize(PETSC_DEVICE_MAX_DEVICES) ") for a specific device", "PetscDeviceCreate()", *id, id, flag, PETSC_DECIDE, PETSC_DEVICE_MAX_DEVICES));
186   PetscFunctionReturn(0);
187 }
188 
189 template <typename D>
190 template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
191 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept
192 {
193   PetscFunctionBegin;
194   PetscCall(PetscOptionDevice(PetscOptionsBool_Private, PetscOptionsObject, "-device_view_", std::forward<T>(args)...));
195   PetscFunctionReturn(0);
196 }
197 
198 template <typename D>
199 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems *PetscOptionsObject, PetscBool *view, PetscBool *flag) noexcept
200 {
201   PetscFunctionBegin;
202   PetscCall(PetscOptionDeviceView(PetscOptionsObject, "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *view, view, flag));
203   PetscFunctionReturn(0);
204 }
205 
206 template <typename D>
207 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceBasic(PetscOptionItems *PetscOptionsObject, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept
208 {
209   PetscFunctionBegin;
210   PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, &initType.first, &initType.second));
211   PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, &initId.first, &initId.second));
212   PetscCall(PetscOptionDeviceView(PetscOptionsObject, &initView.first, &initView.second));
213   PetscFunctionReturn(0);
214 }
215 
216 template <typename D>
217 inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceAll(MPI_Comm comm, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept
218 {
219   constexpr char optname[] = "PetscDevice %s Options";
220   constexpr auto dtype     = PETSC_DEVICE_IMPL();
221   const auto     implname  = PetscDeviceTypes[dtype];
222   auto           buf       = std::array<char, 128>{};
223   constexpr auto buflen    = buf.size() - 1; // -1 to leave room for null
224 
225   PetscFunctionBegin;
226   if (PetscDefined(USE_DEBUG)) {
227     // -3 since '%s' is replaced and dont count null char for optname
228     const auto len = std::strlen(implname) + PETSC_STATIC_ARRAY_LENGTH(optname) - 3;
229 
230     PetscCheck(len < buflen, comm, PETSC_ERR_PLIB, "char buffer is not large enough to hold 'PetscDevice %s Options'; have %zu need %zu", implname, buflen, len);
231   }
232   PetscCall(PetscSNPrintf(buf.data(), buflen, optname, implname));
233   PetscOptionsBegin(comm, nullptr, buf.data(), "Sys");
234   PetscCall(PetscOptionDeviceBasic(PetscOptionsObject, initType, initId, initView));
235   PetscOptionsEnd();
236   PetscFunctionReturn(0);
237 }
238 
239 } // namespace impl
240 
241 } // namespace device
242 
243 } // namespace Petsc
244 
245   #define PETSC_DEVICE_IMPL_BASE_CLASS_HEADER(base_name, T) \
246     using base_name = ::Petsc::device::impl::DeviceBase<T>; \
247     friend base_name; \
248     using base_name::base_name
249 
250 #endif // __cplusplus
251 
252 #endif // IMPLDEVICEBASE_HPP
253