xref: /petsc/src/sys/objects/device/impls/cupm/cupmevent.hpp (revision 96a4b4d95ea58ea02aff154c6b83fc6968de23ca)
10e6b6b59SJacob Faibussowitsch #ifndef PETSC_CUPMEVENT_HPP
20e6b6b59SJacob Faibussowitsch #define PETSC_CUPMEVENT_HPP
30e6b6b59SJacob Faibussowitsch 
40e6b6b59SJacob Faibussowitsch #include <petsc/private/cupminterface.hpp>
50e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp>
60e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/object_pool.hpp>
70e6b6b59SJacob Faibussowitsch 
80e6b6b59SJacob Faibussowitsch #if defined(__cplusplus)
9146a86ebSJacob Faibussowitsch   #include <stack>
10d71ae5a4SJacob Faibussowitsch namespace Petsc
11d71ae5a4SJacob Faibussowitsch {
120e6b6b59SJacob Faibussowitsch 
13d71ae5a4SJacob Faibussowitsch namespace device
14d71ae5a4SJacob Faibussowitsch {
150e6b6b59SJacob Faibussowitsch 
16d71ae5a4SJacob Faibussowitsch namespace cupm
17d71ae5a4SJacob Faibussowitsch {
180e6b6b59SJacob Faibussowitsch 
190e6b6b59SJacob Faibussowitsch // A pool for allocating cupmEvent_t's. While events are generally very cheap to create and
200e6b6b59SJacob Faibussowitsch // destroy, they are not free. Using the pool vs on-demand creation and destruction yields a ~20%
210e6b6b59SJacob Faibussowitsch // speedup.
220e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
23146a86ebSJacob Faibussowitsch class CUPMEventPool : impl::Interface<T>, public RegisterFinalizeable<CUPMEventPool<T, flags>> {
24146a86ebSJacob Faibussowitsch public:
25*96a4b4d9SJacob Faibussowitsch   PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T);
260e6b6b59SJacob Faibussowitsch 
27089fb57cSJacob Faibussowitsch   PetscErrorCode allocate(cupmEvent_t *) noexcept;
28089fb57cSJacob Faibussowitsch   PetscErrorCode deallocate(cupmEvent_t *) noexcept;
29146a86ebSJacob Faibussowitsch 
30089fb57cSJacob Faibussowitsch   PetscErrorCode finalize_() noexcept;
31146a86ebSJacob Faibussowitsch 
32146a86ebSJacob Faibussowitsch private:
33146a86ebSJacob Faibussowitsch   std::stack<cupmEvent_t> pool_;
340e6b6b59SJacob Faibussowitsch };
350e6b6b59SJacob Faibussowitsch 
360e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
37146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::finalize_() noexcept
38d71ae5a4SJacob Faibussowitsch {
390e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
40146a86ebSJacob Faibussowitsch   while (!pool_.empty()) {
41146a86ebSJacob Faibussowitsch     PetscCallCUPM(cupmEventDestroy(std::move(pool_.top())));
42146a86ebSJacob Faibussowitsch     PetscCallCXX(pool_.pop());
43146a86ebSJacob Faibussowitsch   }
443ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
450e6b6b59SJacob Faibussowitsch }
460e6b6b59SJacob Faibussowitsch 
470e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
48146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::allocate(cupmEvent_t *event) noexcept
49d71ae5a4SJacob Faibussowitsch {
500e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
51146a86ebSJacob Faibussowitsch   PetscValidPointer(event, 1);
52146a86ebSJacob Faibussowitsch   if (pool_.empty()) {
53146a86ebSJacob Faibussowitsch     PetscCall(this->register_finalize());
54146a86ebSJacob Faibussowitsch     PetscCallCUPM(cupmEventCreateWithFlags(event, flags));
55146a86ebSJacob Faibussowitsch   } else {
56146a86ebSJacob Faibussowitsch     PetscCallCXX(*event = std::move(pool_.top()));
57146a86ebSJacob Faibussowitsch     PetscCallCXX(pool_.pop());
58146a86ebSJacob Faibussowitsch   }
593ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
600e6b6b59SJacob Faibussowitsch }
610e6b6b59SJacob Faibussowitsch 
62146a86ebSJacob Faibussowitsch template <DeviceType T, unsigned long flags>
63146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::deallocate(cupmEvent_t *in_event) noexcept
64d71ae5a4SJacob Faibussowitsch {
65146a86ebSJacob Faibussowitsch   PetscFunctionBegin;
66146a86ebSJacob Faibussowitsch   PetscValidPointer(in_event, 1);
67146a86ebSJacob Faibussowitsch   if (auto event = std::exchange(*in_event, cupmEvent_t{})) {
68146a86ebSJacob Faibussowitsch     if (this->registered()) {
69146a86ebSJacob Faibussowitsch       PetscCallCXX(pool_.push(std::move(event)));
70146a86ebSJacob Faibussowitsch     } else {
71146a86ebSJacob Faibussowitsch       PetscCallCUPM(cupmEventDestroy(event));
72146a86ebSJacob Faibussowitsch     }
73146a86ebSJacob Faibussowitsch   }
743ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
75146a86ebSJacob Faibussowitsch }
76146a86ebSJacob Faibussowitsch 
77146a86ebSJacob Faibussowitsch template <DeviceType T, unsigned long flags>
78146a86ebSJacob Faibussowitsch CUPMEventPool<T, flags> &cupm_event_pool() noexcept
79146a86ebSJacob Faibussowitsch {
80146a86ebSJacob Faibussowitsch   static CUPMEventPool<T, flags> pool;
810e6b6b59SJacob Faibussowitsch   return pool;
820e6b6b59SJacob Faibussowitsch }
830e6b6b59SJacob Faibussowitsch 
840e6b6b59SJacob Faibussowitsch // pool of events with timing disabled
850e6b6b59SJacob Faibussowitsch template <DeviceType T>
86d71ae5a4SJacob Faibussowitsch inline auto cupm_fast_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>()) &
87d71ae5a4SJacob Faibussowitsch {
880e6b6b59SJacob Faibussowitsch   return cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>();
890e6b6b59SJacob Faibussowitsch }
900e6b6b59SJacob Faibussowitsch 
910e6b6b59SJacob Faibussowitsch // pool of events with timing enabled
920e6b6b59SJacob Faibussowitsch template <DeviceType T>
93d71ae5a4SJacob Faibussowitsch inline auto cupm_timer_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>()) &
94d71ae5a4SJacob Faibussowitsch {
950e6b6b59SJacob Faibussowitsch   return cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>();
960e6b6b59SJacob Faibussowitsch }
970e6b6b59SJacob Faibussowitsch 
980e6b6b59SJacob Faibussowitsch // A simple wrapper of cupmEvent_t. This is used in conjunction with CUPMStream to build the
990e6b6b59SJacob Faibussowitsch // event-stream pairing for the async allocator. It is also used as the data member of
1000e6b6b59SJacob Faibussowitsch // PetscEvent.
1010e6b6b59SJacob Faibussowitsch template <DeviceType T>
1020e6b6b59SJacob Faibussowitsch class CUPMEvent : impl::Interface<T>, public memory::PoolAllocated<CUPMEvent<T>> {
1030e6b6b59SJacob Faibussowitsch   using pool_type = memory::PoolAllocated<CUPMEvent<T>>;
1040e6b6b59SJacob Faibussowitsch 
1050e6b6b59SJacob Faibussowitsch public:
106*96a4b4d9SJacob Faibussowitsch   PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T);
1070e6b6b59SJacob Faibussowitsch 
1080e6b6b59SJacob Faibussowitsch   constexpr CUPMEvent() noexcept = default;
1090e6b6b59SJacob Faibussowitsch   ~CUPMEvent() noexcept;
1100e6b6b59SJacob Faibussowitsch 
1110e6b6b59SJacob Faibussowitsch   CUPMEvent(CUPMEvent &&) noexcept;
1120e6b6b59SJacob Faibussowitsch   CUPMEvent &operator=(CUPMEvent &&) noexcept;
1130e6b6b59SJacob Faibussowitsch 
1140e6b6b59SJacob Faibussowitsch   // event is not copyable
1150e6b6b59SJacob Faibussowitsch   CUPMEvent(const CUPMEvent &)            = delete;
1160e6b6b59SJacob Faibussowitsch   CUPMEvent &operator=(const CUPMEvent &) = delete;
1170e6b6b59SJacob Faibussowitsch 
1180e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD cupmEvent_t get() noexcept;
119089fb57cSJacob Faibussowitsch   PetscErrorCode              record(cupmStream_t) noexcept;
1200e6b6b59SJacob Faibussowitsch 
1210e6b6b59SJacob Faibussowitsch   explicit operator bool() const noexcept;
1220e6b6b59SJacob Faibussowitsch 
1230e6b6b59SJacob Faibussowitsch private:
1240e6b6b59SJacob Faibussowitsch   cupmEvent_t event_{};
1250e6b6b59SJacob Faibussowitsch };
1260e6b6b59SJacob Faibussowitsch 
1270e6b6b59SJacob Faibussowitsch template <DeviceType T>
128d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T>::~CUPMEvent() noexcept
129d71ae5a4SJacob Faibussowitsch {
1300e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
131146a86ebSJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().deallocate(&event_));
1320e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
1330e6b6b59SJacob Faibussowitsch }
1340e6b6b59SJacob Faibussowitsch 
1350e6b6b59SJacob Faibussowitsch template <DeviceType T>
136*96a4b4d9SJacob Faibussowitsch inline CUPMEvent<T>::CUPMEvent(CUPMEvent &&other) noexcept : pool_type(std::move(other)), event_(util::exchange(other.event_, cupmEvent_t{}))
137d71ae5a4SJacob Faibussowitsch {
138*96a4b4d9SJacob Faibussowitsch   static_assert(std::is_empty<impl::Interface<T>>::value, "");
139d71ae5a4SJacob Faibussowitsch }
1400e6b6b59SJacob Faibussowitsch 
1410e6b6b59SJacob Faibussowitsch template <DeviceType T>
142d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T> &CUPMEvent<T>::operator=(CUPMEvent &&other) noexcept
143d71ae5a4SJacob Faibussowitsch {
1440e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1450e6b6b59SJacob Faibussowitsch   if (this != &other) {
1460e6b6b59SJacob Faibussowitsch     pool_type::operator=(std::move(other));
147146a86ebSJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().deallocate(&event_));
1480e6b6b59SJacob Faibussowitsch     event_ = util::exchange(other.event_, cupmEvent_t{});
1490e6b6b59SJacob Faibussowitsch   }
1500e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
1510e6b6b59SJacob Faibussowitsch }
1520e6b6b59SJacob Faibussowitsch 
1530e6b6b59SJacob Faibussowitsch template <DeviceType T>
154d71ae5a4SJacob Faibussowitsch inline typename CUPMEvent<T>::cupmEvent_t CUPMEvent<T>::get() noexcept
155d71ae5a4SJacob Faibussowitsch {
1560e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1570e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(!event_)) PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().allocate(&event_));
1580e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(event_);
1590e6b6b59SJacob Faibussowitsch }
1600e6b6b59SJacob Faibussowitsch 
1610e6b6b59SJacob Faibussowitsch template <DeviceType T>
162d71ae5a4SJacob Faibussowitsch inline PetscErrorCode CUPMEvent<T>::record(cupmStream_t stream) noexcept
163d71ae5a4SJacob Faibussowitsch {
1640e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1650e6b6b59SJacob Faibussowitsch   PetscCallCUPM(cupmEventRecord(get(), stream));
1663ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1670e6b6b59SJacob Faibussowitsch }
1680e6b6b59SJacob Faibussowitsch 
1690e6b6b59SJacob Faibussowitsch template <DeviceType T>
170d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T>::operator bool() const noexcept
171d71ae5a4SJacob Faibussowitsch {
1720e6b6b59SJacob Faibussowitsch   return event_ != cupmEvent_t{};
1730e6b6b59SJacob Faibussowitsch }
1740e6b6b59SJacob Faibussowitsch 
1750e6b6b59SJacob Faibussowitsch } // namespace cupm
1760e6b6b59SJacob Faibussowitsch 
1770e6b6b59SJacob Faibussowitsch } // namespace device
1780e6b6b59SJacob Faibussowitsch 
1790e6b6b59SJacob Faibussowitsch } // namespace Petsc
1800e6b6b59SJacob Faibussowitsch #endif // __cplusplus
1810e6b6b59SJacob Faibussowitsch 
1820e6b6b59SJacob Faibussowitsch #endif // PETSC_CUPMEVENT_HPP
183