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