1*0e6b6b59SJacob Faibussowitsch #ifndef PETSC_CUPMEVENT_HPP 2*0e6b6b59SJacob Faibussowitsch #define PETSC_CUPMEVENT_HPP 3*0e6b6b59SJacob Faibussowitsch 4*0e6b6b59SJacob Faibussowitsch #include <petsc/private/cupminterface.hpp> 5*0e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp> 6*0e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/object_pool.hpp> 7*0e6b6b59SJacob Faibussowitsch 8*0e6b6b59SJacob Faibussowitsch #if defined(__cplusplus) 9*0e6b6b59SJacob Faibussowitsch namespace Petsc { 10*0e6b6b59SJacob Faibussowitsch 11*0e6b6b59SJacob Faibussowitsch namespace device { 12*0e6b6b59SJacob Faibussowitsch 13*0e6b6b59SJacob Faibussowitsch namespace cupm { 14*0e6b6b59SJacob Faibussowitsch 15*0e6b6b59SJacob Faibussowitsch namespace { 16*0e6b6b59SJacob Faibussowitsch 17*0e6b6b59SJacob Faibussowitsch // A pool for allocating cupmEvent_t's. While events are generally very cheap to create and 18*0e6b6b59SJacob Faibussowitsch // destroy, they are not free. Using the pool vs on-demand creation and destruction yields a ~20% 19*0e6b6b59SJacob Faibussowitsch // speedup. 20*0e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags> 21*0e6b6b59SJacob Faibussowitsch struct CUPMEventPoolAllocator : impl::Interface<T>, AllocatorBase<typename impl::Interface<T>::cupmEvent_t> { 22*0e6b6b59SJacob Faibussowitsch PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(interface_type, T); 23*0e6b6b59SJacob Faibussowitsch 24*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD static PetscErrorCode create(cupmEvent_t *) noexcept; 25*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD static PetscErrorCode destroy(cupmEvent_t) noexcept; 26*0e6b6b59SJacob Faibussowitsch }; 27*0e6b6b59SJacob Faibussowitsch 28*0e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags> 29*0e6b6b59SJacob Faibussowitsch inline PetscErrorCode CUPMEventPoolAllocator<T, flags>::create(cupmEvent_t *event) noexcept { 30*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 31*0e6b6b59SJacob Faibussowitsch PetscCallCUPM(cupmEventCreateWithFlags(event, flags)); 32*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 33*0e6b6b59SJacob Faibussowitsch } 34*0e6b6b59SJacob Faibussowitsch 35*0e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags> 36*0e6b6b59SJacob Faibussowitsch inline PetscErrorCode CUPMEventPoolAllocator<T, flags>::destroy(cupmEvent_t event) noexcept { 37*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 38*0e6b6b59SJacob Faibussowitsch PetscCallCUPM(cupmEventDestroy(event)); 39*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 40*0e6b6b59SJacob Faibussowitsch } 41*0e6b6b59SJacob Faibussowitsch 42*0e6b6b59SJacob Faibussowitsch } // anonymous namespace 43*0e6b6b59SJacob Faibussowitsch 44*0e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags, typename allocator_type = CUPMEventPoolAllocator<T, flags>, typename pool_type = ObjectPool<typename allocator_type::value_type, allocator_type>> 45*0e6b6b59SJacob Faibussowitsch pool_type &cupm_event_pool() noexcept { 46*0e6b6b59SJacob Faibussowitsch static pool_type pool; 47*0e6b6b59SJacob Faibussowitsch return pool; 48*0e6b6b59SJacob Faibussowitsch } 49*0e6b6b59SJacob Faibussowitsch 50*0e6b6b59SJacob Faibussowitsch // pool of events with timing disabled 51*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 52*0e6b6b59SJacob Faibussowitsch inline auto cupm_fast_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>()) & { 53*0e6b6b59SJacob Faibussowitsch return cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>(); 54*0e6b6b59SJacob Faibussowitsch } 55*0e6b6b59SJacob Faibussowitsch 56*0e6b6b59SJacob Faibussowitsch // pool of events with timing enabled 57*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 58*0e6b6b59SJacob Faibussowitsch inline auto cupm_timer_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>()) & { 59*0e6b6b59SJacob Faibussowitsch return cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>(); 60*0e6b6b59SJacob Faibussowitsch } 61*0e6b6b59SJacob Faibussowitsch 62*0e6b6b59SJacob Faibussowitsch // A simple wrapper of cupmEvent_t. This is used in conjunction with CUPMStream to build the 63*0e6b6b59SJacob Faibussowitsch // event-stream pairing for the async allocator. It is also used as the data member of 64*0e6b6b59SJacob Faibussowitsch // PetscEvent. 65*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 66*0e6b6b59SJacob Faibussowitsch class CUPMEvent : impl::Interface<T>, public memory::PoolAllocated<CUPMEvent<T>> { 67*0e6b6b59SJacob Faibussowitsch using pool_type = memory::PoolAllocated<CUPMEvent<T>>; 68*0e6b6b59SJacob Faibussowitsch 69*0e6b6b59SJacob Faibussowitsch public: 70*0e6b6b59SJacob Faibussowitsch PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(interface_type, T); 71*0e6b6b59SJacob Faibussowitsch 72*0e6b6b59SJacob Faibussowitsch constexpr CUPMEvent() noexcept = default; 73*0e6b6b59SJacob Faibussowitsch ~CUPMEvent() noexcept; 74*0e6b6b59SJacob Faibussowitsch 75*0e6b6b59SJacob Faibussowitsch CUPMEvent(CUPMEvent &&) noexcept; 76*0e6b6b59SJacob Faibussowitsch CUPMEvent &operator=(CUPMEvent &&) noexcept; 77*0e6b6b59SJacob Faibussowitsch 78*0e6b6b59SJacob Faibussowitsch // event is not copyable 79*0e6b6b59SJacob Faibussowitsch CUPMEvent(const CUPMEvent &) = delete; 80*0e6b6b59SJacob Faibussowitsch CUPMEvent &operator=(const CUPMEvent &) = delete; 81*0e6b6b59SJacob Faibussowitsch 82*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD cupmEvent_t get() noexcept; 83*0e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode record(cupmStream_t) noexcept; 84*0e6b6b59SJacob Faibussowitsch 85*0e6b6b59SJacob Faibussowitsch explicit operator bool() const noexcept; 86*0e6b6b59SJacob Faibussowitsch 87*0e6b6b59SJacob Faibussowitsch private: 88*0e6b6b59SJacob Faibussowitsch cupmEvent_t event_{}; 89*0e6b6b59SJacob Faibussowitsch }; 90*0e6b6b59SJacob Faibussowitsch 91*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 92*0e6b6b59SJacob Faibussowitsch inline CUPMEvent<T>::~CUPMEvent() noexcept { 93*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 94*0e6b6b59SJacob Faibussowitsch if (event_) PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().deallocate(std::move(event_))); 95*0e6b6b59SJacob Faibussowitsch PetscFunctionReturnVoid(); 96*0e6b6b59SJacob Faibussowitsch } 97*0e6b6b59SJacob Faibussowitsch 98*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 99*0e6b6b59SJacob Faibussowitsch inline CUPMEvent<T>::CUPMEvent(CUPMEvent &&other) noexcept : interface_type(std::move(other)), pool_type(std::move(other)), event_(util::exchange(other.event_, cupmEvent_t{})) { } 100*0e6b6b59SJacob Faibussowitsch 101*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 102*0e6b6b59SJacob Faibussowitsch inline CUPMEvent<T> &CUPMEvent<T>::operator=(CUPMEvent &&other) noexcept { 103*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 104*0e6b6b59SJacob Faibussowitsch if (this != &other) { 105*0e6b6b59SJacob Faibussowitsch interface_type::operator=(std::move(other)); 106*0e6b6b59SJacob Faibussowitsch pool_type:: operator=(std::move(other)); 107*0e6b6b59SJacob Faibussowitsch if (event_) PetscCall(cupm_fast_event_pool<T>().deallocate(std::move(event_))); 108*0e6b6b59SJacob Faibussowitsch event_ = util::exchange(other.event_, cupmEvent_t{}); 109*0e6b6b59SJacob Faibussowitsch } 110*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(*this); 111*0e6b6b59SJacob Faibussowitsch } 112*0e6b6b59SJacob Faibussowitsch 113*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 114*0e6b6b59SJacob Faibussowitsch inline typename CUPMEvent<T>::cupmEvent_t CUPMEvent<T>::get() noexcept { 115*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 116*0e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!event_)) PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().allocate(&event_)); 117*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(event_); 118*0e6b6b59SJacob Faibussowitsch } 119*0e6b6b59SJacob Faibussowitsch 120*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 121*0e6b6b59SJacob Faibussowitsch inline PetscErrorCode CUPMEvent<T>::record(cupmStream_t stream) noexcept { 122*0e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 123*0e6b6b59SJacob Faibussowitsch PetscCallCUPM(cupmEventRecord(get(), stream)); 124*0e6b6b59SJacob Faibussowitsch PetscFunctionReturn(0); 125*0e6b6b59SJacob Faibussowitsch } 126*0e6b6b59SJacob Faibussowitsch 127*0e6b6b59SJacob Faibussowitsch template <DeviceType T> 128*0e6b6b59SJacob Faibussowitsch inline CUPMEvent<T>::operator bool() const noexcept { 129*0e6b6b59SJacob Faibussowitsch return event_ != cupmEvent_t{}; 130*0e6b6b59SJacob Faibussowitsch } 131*0e6b6b59SJacob Faibussowitsch 132*0e6b6b59SJacob Faibussowitsch } // namespace cupm 133*0e6b6b59SJacob Faibussowitsch 134*0e6b6b59SJacob Faibussowitsch } // namespace device 135*0e6b6b59SJacob Faibussowitsch 136*0e6b6b59SJacob Faibussowitsch } // namespace Petsc 137*0e6b6b59SJacob Faibussowitsch #endif // __cplusplus 138*0e6b6b59SJacob Faibussowitsch 139*0e6b6b59SJacob Faibussowitsch #endif // PETSC_CUPMEVENT_HPP 140