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