Actual source code: impldevicebase.hpp

  1: #ifndef IMPLDEVICEBASE_HPP
  2: #define IMPLDEVICEBASE_HPP

  4: #if defined(__cplusplus)
  5: #include <petsc/private/deviceimpl.h>
  6: #include <petsc/private/viewerimpl.h>

  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>

 13:   #include <cstring> // for std::strlen

 15: namespace Petsc
 16: {

 18: namespace device
 19: {

 21: namespace impl
 22: {

 24: template <typename Derived> // CRTP
 25: class DeviceBase : public util::crtp<DeviceBase, Derived> {
 26: public:
 27:   using derived_type            = Derived;
 28:   using createContextFunction_t = PetscErrorCode (*)(PetscDeviceContext);

 30:   // default constructor
 31:   constexpr DeviceBase(createContextFunction_t f) noexcept : create_(f) { }

 33:   template <typename T = derived_type>
 34:   PETSC_NODISCARD static constexpr PetscDeviceType PETSC_DEVICE_IMPL() noexcept
 35:   {
 36:     return T::PETSC_DEVICE_IMPL_();
 37:   }

 39:   PetscErrorCode        getDevice(PetscDevice, PetscInt) noexcept;
 40:   static PetscErrorCode configureDevice(PetscDevice) noexcept;
 41:   static PetscErrorCode viewDevice(PetscDevice, PetscViewer) noexcept;
 42:   static PetscErrorCode getAttribute(PetscDevice, PetscDeviceAttribute, void *) noexcept;

 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_;

 49:   // if you want the base class to handle the entire options query, has the same arguments as
 50:   // PetscOptionDeviceBasic
 51:   static PetscErrorCode PetscOptionDeviceAll(MPI_Comm, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept;

 53:   // if you want to start and end the options query yourself, but still want all the default
 54:   // options
 55:   static PetscErrorCode PetscOptionDeviceBasic(PetscOptionItems *, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept;

 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

 64:   // option template for initializing the device
 65:   static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, PetscDeviceInitType *, PetscBool *) noexcept;
 66:   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
 67:   static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, T &&...) noexcept;
 68:   // option template for selecting the default device
 69:   static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, PetscInt *, PetscBool *) noexcept;
 70:   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
 71:   static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, T &&...) noexcept;
 72:   // option templates for viewing a device
 73:   static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, PetscBool *, PetscBool *) noexcept;
 74:   template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0>
 75:   static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, T &&...) noexcept;

 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:   static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems *, const char[], T &&...) noexcept;

 83:   // default crtp implementations
 84:   static PetscErrorCode init_device_id_(PetscInt *id) noexcept
 85:   {
 86:     PetscFunctionBegin;
 87:     *id = 0;
 88:     PetscFunctionReturn(PETSC_SUCCESS);
 89:   }

 91:   static constexpr PetscErrorCode configure_device_(PetscDevice) noexcept { return PETSC_SUCCESS; }
 92:   static constexpr PetscErrorCode view_device_(PetscDevice, PetscViewer) noexcept { return PETSC_SUCCESS; }
 93: };

 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(PETSC_SUCCESS);
106: }

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(PETSC_SUCCESS);
114: }

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(PETSC_SUCCESS);
122: }

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(PETSC_SUCCESS);
130: }

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;

141:   PetscFunctionBegin;
142:   if (PetscDefined(USE_DEBUG)) {
143:     const auto len = std::strlen(optstub) + std::strlen(implname);

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(PETSC_SUCCESS);
150: }

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(PETSC_SUCCESS);
159: }

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::to_underlying(*inittype));

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(PETSC_SUCCESS);
170: }

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(PETSC_SUCCESS);
179: }

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(PETSC_SUCCESS);
187: }

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(PETSC_SUCCESS);
196: }

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(PETSC_SUCCESS);
204: }

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(PETSC_SUCCESS);
214: }

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

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;

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(PETSC_SUCCESS);
237: }

239: } // namespace impl

241: } // namespace device

243: } // namespace Petsc

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

250: #endif // __cplusplus

252: #endif // IMPLDEVICEBASE_HPP