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<DeviceBase, Derived> { 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 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; 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 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 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 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; 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 static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems *, const char[], T &&...) noexcept; 82 83 // default crtp implementations 84 static PetscErrorCode init_device_id_(PetscInt *id) noexcept 85 { 86 PetscFunctionBegin; 87 *id = 0; 88 PetscFunctionReturn(PETSC_SUCCESS); 89 } 90 91 static constexpr PetscErrorCode configure_device_(PetscDevice) noexcept { return PETSC_SUCCESS; } 92 static constexpr PetscErrorCode view_device_(PetscDevice, PetscViewer) noexcept { return PETSC_SUCCESS; } 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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::to_underlying(*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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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(PETSC_SUCCESS); 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