xref: /petsc/src/sys/objects/device/impls/cupm/cupmevent.hpp (revision f1580f4e3ce5d5b2393648fd039d0d41b440385d)
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