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