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