Actual source code: impldevicebase.hpp

      1: #pragma once

      3: #include <petsc/private/deviceimpl.h>
      4: #include <petsc/private/viewerimpl.h>

      6: #include <petsc/private/cpp/crtp.hpp>
      7: #include <petsc/private/cpp/type_traits.hpp>
      8: #include <petsc/private/cpp/utility.hpp>
      9: #include <petsc/private/cpp/array.hpp>

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

     13: namespace Petsc
     14: {

     16: namespace device
     17: {

     19: namespace impl
     20: {

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

     28:   // default constructor
     29:   constexpr DeviceBase(createContextFunction_t f) noexcept : create_(f) { }

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

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

     42: protected:
     43:   // function to create a PetscDeviceContext (the (*create) function pointer usually set
     44:   // via XXXSetType() for other PETSc objects)
     45:   const createContextFunction_t create_;

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

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

     55:   // option templates to follow, each one has two forms:
     56:   // - A simple form returning only the value and flag. This gives no control over the message,
     57:   //   arguments to the options query or otherwise
     58:   // - A complex form, which allows you to pass most of the options query arguments *EXCEPT*
     59:   //   - The options query function called
     60:   //   - The option string

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

     75: private:
     76:   // base function for all options templates above, they basically just reformat the arguments,
     77:   // create the option string and pass it off to this function
     78:   template <typename... T, typename F = PetscErrorCode (*)(PetscOptionItems, const char *, T &&...)>
     79:   static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems, const char[], T &&...) noexcept;

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

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

     93: template <typename D>
     94: inline PetscErrorCode DeviceBase<D>::getDevice(PetscDevice device, PetscInt id) noexcept
     95: {
     96:   PetscFunctionBegin;
     97:   PetscCall(this->underlying().init_device_id_(&id));
     98:   device->deviceId           = id;
     99:   device->ops->createcontext = this->underlying().create_;
    100:   device->ops->configure     = this->underlying().configureDevice;
    101:   device->ops->view          = this->underlying().viewDevice;
    102:   device->ops->getattribute  = this->underlying().getAttribute;
    103:   PetscFunctionReturn(PETSC_SUCCESS);
    104: }

    106: template <typename D>
    107: inline PetscErrorCode DeviceBase<D>::configureDevice(PetscDevice device) noexcept
    108: {
    109:   PetscFunctionBegin;
    110:   PetscCall(derived_type::configure_device_(device));
    111:   PetscFunctionReturn(PETSC_SUCCESS);
    112: }

    114: template <typename D>
    115: inline PetscErrorCode DeviceBase<D>::viewDevice(PetscDevice device, PetscViewer viewer) noexcept
    116: {
    117:   PetscFunctionBegin;
    118:   PetscCall(derived_type::view_device_(device, viewer));
    119:   PetscFunctionReturn(PETSC_SUCCESS);
    120: }

    122: template <typename D>
    123: inline PetscErrorCode DeviceBase<D>::getAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) noexcept
    124: {
    125:   PetscFunctionBegin;
    126:   PetscCall(derived_type::get_attribute_(device->deviceId, attr, value));
    127:   PetscFunctionReturn(PETSC_SUCCESS);
    128: }

    130: template <typename D>
    131: template <typename... T, typename F>
    132: inline PetscErrorCode DeviceBase<D>::PetscOptionDevice(F &&OptionsFunction, PetscOptionItems PetscOptionsObject, const char optstub[], T &&...args) noexcept
    133: {
    134:   constexpr auto dtype    = PETSC_DEVICE_IMPL();
    135:   const auto     implname = PetscDeviceTypes[dtype];
    136:   auto           buf      = std::array<char, 128>{};
    137:   constexpr auto buflen   = buf.size() - 1;

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

    143:     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);
    144:   }
    145:   PetscCall(PetscSNPrintf(buf.data(), buflen, "%s%s", optstub, implname));
    146:   PetscCall(OptionsFunction(PetscOptionsObject, buf.data(), std::forward<T>(args)...));
    147:   PetscFunctionReturn(PETSC_SUCCESS);
    148: }

    150: template <typename D>
    151: template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
    152: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems PetscOptionsObject, T &&...args) noexcept
    153: {
    154:   PetscFunctionBegin;
    155:   PetscCall(PetscOptionDevice(PetscOptionsEList_Private, PetscOptionsObject, "-device_enable_", std::forward<T>(args)...));
    156:   PetscFunctionReturn(PETSC_SUCCESS);
    157: }

    159: template <typename D>
    160: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems PetscOptionsObject, PetscDeviceInitType *inittype, PetscBool *flag) noexcept
    161: {
    162:   auto type = static_cast<PetscInt>(util::to_underlying(*inittype));

    164:   PetscFunctionBegin;
    165:   PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, "How (or whether) to initialize a device", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[type], &type, flag));
    166:   *inittype = static_cast<PetscDeviceInitType>(type);
    167:   PetscFunctionReturn(PETSC_SUCCESS);
    168: }

    170: template <typename D>
    171: template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
    172: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems PetscOptionsObject, T &&...args) noexcept
    173: {
    174:   PetscFunctionBegin;
    175:   PetscCall(PetscOptionDevice(PetscOptionsInt_Private, PetscOptionsObject, "-device_select_", std::forward<T>(args)...));
    176:   PetscFunctionReturn(PETSC_SUCCESS);
    177: }

    179: template <typename D>
    180: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems PetscOptionsObject, PetscInt *id, PetscBool *flag) noexcept
    181: {
    182:   PetscFunctionBegin;
    183:   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));
    184:   PetscFunctionReturn(PETSC_SUCCESS);
    185: }

    187: template <typename D>
    188: template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>>
    189: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems PetscOptionsObject, T &&...args) noexcept
    190: {
    191:   PetscFunctionBegin;
    192:   PetscCall(PetscOptionDevice(PetscOptionsBool_Private, PetscOptionsObject, "-device_view_", std::forward<T>(args)...));
    193:   PetscFunctionReturn(PETSC_SUCCESS);
    194: }

    196: template <typename D>
    197: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems PetscOptionsObject, PetscBool *view, PetscBool *flag) noexcept
    198: {
    199:   PetscFunctionBegin;
    200:   PetscCall(PetscOptionDeviceView(PetscOptionsObject, "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *view, view, flag));
    201:   PetscFunctionReturn(PETSC_SUCCESS);
    202: }

    204: template <typename D>
    205: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceBasic(PetscOptionItems PetscOptionsObject, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept
    206: {
    207:   PetscFunctionBegin;
    208:   PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, &initType.first, &initType.second));
    209:   PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, &initId.first, &initId.second));
    210:   PetscCall(PetscOptionDeviceView(PetscOptionsObject, &initView.first, &initView.second));
    211:   PetscFunctionReturn(PETSC_SUCCESS);
    212: }

    214: template <typename D>
    215: inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceAll(MPI_Comm comm, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept
    216: {
    217:   constexpr char optname[] = "PetscDevice %s Options";
    218:   constexpr auto dtype     = PETSC_DEVICE_IMPL();
    219:   const auto     implname  = PetscDeviceTypes[dtype];
    220:   auto           buf       = std::array<char, 128>{};
    221:   constexpr auto buflen    = buf.size() - 1; // -1 to leave room for null

    223:   PetscFunctionBegin;
    224:   if (PetscDefined(USE_DEBUG)) {
    225:     // -3 since '%s' is replaced and dont count null char for optname
    226:     const auto len = std::strlen(implname) + PETSC_STATIC_ARRAY_LENGTH(optname) - 3;

    228:     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);
    229:   }
    230:   PetscCall(PetscSNPrintf(buf.data(), buflen, optname, implname));
    231:   PetscOptionsBegin(comm, nullptr, buf.data(), "Sys");
    232:   PetscCall(PetscOptionDeviceBasic(PetscOptionsObject, initType, initId, initView));
    233:   PetscOptionsEnd();
    234:   PetscFunctionReturn(PETSC_SUCCESS);
    235: }

    237: } // namespace impl

    239: } // namespace device

    241: } // namespace Petsc

    243: #define PETSC_DEVICE_IMPL_BASE_CLASS_HEADER(base_name, T) \
    244:   using base_name = ::Petsc::device::impl::DeviceBase<T>; \
    245:   friend base_name; \
    246:   using base_name::base_name