1a4963045SJacob Faibussowitsch #pragma once
20e6b6b59SJacob Faibussowitsch
30e6b6b59SJacob Faibussowitsch #include <petsc/private/cupminterface.hpp>
40e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp>
50e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/object_pool.hpp>
60e6b6b59SJacob Faibussowitsch
7146a86ebSJacob Faibussowitsch #include <stack>
8f13b9fe2SJacob Faibussowitsch
9d71ae5a4SJacob Faibussowitsch namespace Petsc
10d71ae5a4SJacob Faibussowitsch {
110e6b6b59SJacob Faibussowitsch
12d71ae5a4SJacob Faibussowitsch namespace device
13d71ae5a4SJacob Faibussowitsch {
140e6b6b59SJacob Faibussowitsch
15d71ae5a4SJacob Faibussowitsch namespace cupm
16d71ae5a4SJacob Faibussowitsch {
170e6b6b59SJacob Faibussowitsch
180e6b6b59SJacob Faibussowitsch // A pool for allocating cupmEvent_t's. While events are generally very cheap to create and
190e6b6b59SJacob Faibussowitsch // destroy, they are not free. Using the pool vs on-demand creation and destruction yields a ~20%
200e6b6b59SJacob Faibussowitsch // speedup.
210e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
2285f25e71SJed Brown class PETSC_SINGLE_LIBRARY_VISIBILITY_INTERNAL CUPMEventPool : impl::Interface<T>, public RegisterFinalizeable<CUPMEventPool<T, flags>> {
23146a86ebSJacob Faibussowitsch public:
2496a4b4d9SJacob Faibussowitsch PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T);
250e6b6b59SJacob Faibussowitsch
26089fb57cSJacob Faibussowitsch PetscErrorCode allocate(cupmEvent_t *) noexcept;
27089fb57cSJacob Faibussowitsch PetscErrorCode deallocate(cupmEvent_t *) noexcept;
28146a86ebSJacob Faibussowitsch
29089fb57cSJacob Faibussowitsch PetscErrorCode finalize_() noexcept;
30146a86ebSJacob Faibussowitsch
31146a86ebSJacob Faibussowitsch private:
32146a86ebSJacob Faibussowitsch std::stack<cupmEvent_t> pool_;
330e6b6b59SJacob Faibussowitsch };
340e6b6b59SJacob Faibussowitsch
350e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
finalize_()36146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::finalize_() noexcept
37d71ae5a4SJacob Faibussowitsch {
380e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
39146a86ebSJacob Faibussowitsch while (!pool_.empty()) {
40146a86ebSJacob Faibussowitsch PetscCallCUPM(cupmEventDestroy(std::move(pool_.top())));
41146a86ebSJacob Faibussowitsch PetscCallCXX(pool_.pop());
42146a86ebSJacob Faibussowitsch }
433ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
440e6b6b59SJacob Faibussowitsch }
450e6b6b59SJacob Faibussowitsch
460e6b6b59SJacob Faibussowitsch template <DeviceType T, unsigned long flags>
allocate(cupmEvent_t * event)47146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::allocate(cupmEvent_t *event) noexcept
48d71ae5a4SJacob Faibussowitsch {
490e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
504f572ea9SToby Isaac PetscAssertPointer(event, 1);
51146a86ebSJacob Faibussowitsch if (pool_.empty()) {
52146a86ebSJacob Faibussowitsch PetscCall(this->register_finalize());
53*6497c311SBarry Smith PetscCallCUPM(cupmEventCreateWithFlags(event, (unsigned int)flags));
54146a86ebSJacob Faibussowitsch } else {
55146a86ebSJacob Faibussowitsch PetscCallCXX(*event = std::move(pool_.top()));
56146a86ebSJacob Faibussowitsch PetscCallCXX(pool_.pop());
57146a86ebSJacob Faibussowitsch }
583ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
590e6b6b59SJacob Faibussowitsch }
600e6b6b59SJacob Faibussowitsch
61146a86ebSJacob Faibussowitsch template <DeviceType T, unsigned long flags>
deallocate(cupmEvent_t * in_event)62146a86ebSJacob Faibussowitsch inline PetscErrorCode CUPMEventPool<T, flags>::deallocate(cupmEvent_t *in_event) noexcept
63d71ae5a4SJacob Faibussowitsch {
64146a86ebSJacob Faibussowitsch PetscFunctionBegin;
654f572ea9SToby Isaac PetscAssertPointer(in_event, 1);
66146a86ebSJacob Faibussowitsch if (auto event = std::exchange(*in_event, cupmEvent_t{})) {
67146a86ebSJacob Faibussowitsch if (this->registered()) {
68146a86ebSJacob Faibussowitsch PetscCallCXX(pool_.push(std::move(event)));
69146a86ebSJacob Faibussowitsch } else {
70146a86ebSJacob Faibussowitsch PetscCallCUPM(cupmEventDestroy(event));
71146a86ebSJacob Faibussowitsch }
72146a86ebSJacob Faibussowitsch }
733ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
74146a86ebSJacob Faibussowitsch }
75146a86ebSJacob Faibussowitsch
76146a86ebSJacob Faibussowitsch template <DeviceType T, unsigned long flags>
cupm_event_pool()77146a86ebSJacob Faibussowitsch CUPMEventPool<T, flags> &cupm_event_pool() noexcept
78146a86ebSJacob Faibussowitsch {
79146a86ebSJacob Faibussowitsch static CUPMEventPool<T, flags> pool;
800e6b6b59SJacob Faibussowitsch return pool;
810e6b6b59SJacob Faibussowitsch }
820e6b6b59SJacob Faibussowitsch
830e6b6b59SJacob Faibussowitsch // pool of events with timing disabled
840e6b6b59SJacob Faibussowitsch template <DeviceType T>
cupm_fast_event_pool()85d71ae5a4SJacob Faibussowitsch inline auto cupm_fast_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>()) &
86d71ae5a4SJacob Faibussowitsch {
870e6b6b59SJacob Faibussowitsch return cupm_event_pool<T, impl::Interface<T>::cupmEventDisableTiming>();
880e6b6b59SJacob Faibussowitsch }
890e6b6b59SJacob Faibussowitsch
900e6b6b59SJacob Faibussowitsch // pool of events with timing enabled
910e6b6b59SJacob Faibussowitsch template <DeviceType T>
cupm_timer_event_pool()92d71ae5a4SJacob Faibussowitsch inline auto cupm_timer_event_pool() noexcept -> decltype(cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>()) &
93d71ae5a4SJacob Faibussowitsch {
940e6b6b59SJacob Faibussowitsch return cupm_event_pool<T, impl::Interface<T>::cupmEventDefault>();
950e6b6b59SJacob Faibussowitsch }
960e6b6b59SJacob Faibussowitsch
970e6b6b59SJacob Faibussowitsch // A simple wrapper of cupmEvent_t. This is used in conjunction with CUPMStream to build the
980e6b6b59SJacob Faibussowitsch // event-stream pairing for the async allocator. It is also used as the data member of
990e6b6b59SJacob Faibussowitsch // PetscEvent.
1000e6b6b59SJacob Faibussowitsch template <DeviceType T>
10185f25e71SJed Brown class PETSC_SINGLE_LIBRARY_VISIBILITY_INTERNAL CUPMEvent : impl::Interface<T>, public memory::PoolAllocated {
1023048253cSJacob Faibussowitsch using pool_type = memory::PoolAllocated;
1030e6b6b59SJacob Faibussowitsch
1040e6b6b59SJacob Faibussowitsch public:
10596a4b4d9SJacob Faibussowitsch PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T);
1060e6b6b59SJacob Faibussowitsch
1070e6b6b59SJacob Faibussowitsch constexpr CUPMEvent() noexcept = default;
1080e6b6b59SJacob Faibussowitsch ~CUPMEvent() noexcept;
1090e6b6b59SJacob Faibussowitsch
1100e6b6b59SJacob Faibussowitsch CUPMEvent(CUPMEvent &&) noexcept;
1110e6b6b59SJacob Faibussowitsch CUPMEvent &operator=(CUPMEvent &&) noexcept;
1120e6b6b59SJacob Faibussowitsch
1130e6b6b59SJacob Faibussowitsch // event is not copyable
1140e6b6b59SJacob Faibussowitsch CUPMEvent(const CUPMEvent &) = delete;
1150e6b6b59SJacob Faibussowitsch CUPMEvent &operator=(const CUPMEvent &) = delete;
1160e6b6b59SJacob Faibussowitsch
1170e6b6b59SJacob Faibussowitsch PETSC_NODISCARD cupmEvent_t get() noexcept;
118089fb57cSJacob Faibussowitsch PetscErrorCode record(cupmStream_t) noexcept;
1190e6b6b59SJacob Faibussowitsch
1200e6b6b59SJacob Faibussowitsch explicit operator bool() const noexcept;
1210e6b6b59SJacob Faibussowitsch
1220e6b6b59SJacob Faibussowitsch private:
1230e6b6b59SJacob Faibussowitsch cupmEvent_t event_{};
1240e6b6b59SJacob Faibussowitsch };
1250e6b6b59SJacob Faibussowitsch
1260e6b6b59SJacob Faibussowitsch template <DeviceType T>
~CUPMEvent()127d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T>::~CUPMEvent() noexcept
128d71ae5a4SJacob Faibussowitsch {
1290e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
130146a86ebSJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().deallocate(&event_));
1310e6b6b59SJacob Faibussowitsch PetscFunctionReturnVoid();
1320e6b6b59SJacob Faibussowitsch }
1330e6b6b59SJacob Faibussowitsch
1340e6b6b59SJacob Faibussowitsch template <DeviceType T>
CUPMEvent(CUPMEvent && other)13596a4b4d9SJacob Faibussowitsch inline CUPMEvent<T>::CUPMEvent(CUPMEvent &&other) noexcept : pool_type(std::move(other)), event_(util::exchange(other.event_, cupmEvent_t{}))
136d71ae5a4SJacob Faibussowitsch {
13796a4b4d9SJacob Faibussowitsch static_assert(std::is_empty<impl::Interface<T>>::value, "");
138d71ae5a4SJacob Faibussowitsch }
1390e6b6b59SJacob Faibussowitsch
1400e6b6b59SJacob Faibussowitsch template <DeviceType T>
operator =(CUPMEvent && other)141d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T> &CUPMEvent<T>::operator=(CUPMEvent &&other) noexcept
142d71ae5a4SJacob Faibussowitsch {
1430e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
1440e6b6b59SJacob Faibussowitsch if (this != &other) {
1450e6b6b59SJacob Faibussowitsch pool_type::operator=(std::move(other));
146146a86ebSJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().deallocate(&event_));
1470e6b6b59SJacob Faibussowitsch event_ = util::exchange(other.event_, cupmEvent_t{});
1480e6b6b59SJacob Faibussowitsch }
1490e6b6b59SJacob Faibussowitsch PetscFunctionReturn(*this);
1500e6b6b59SJacob Faibussowitsch }
1510e6b6b59SJacob Faibussowitsch
1520e6b6b59SJacob Faibussowitsch template <DeviceType T>
get()153d71ae5a4SJacob Faibussowitsch inline typename CUPMEvent<T>::cupmEvent_t CUPMEvent<T>::get() noexcept
154d71ae5a4SJacob Faibussowitsch {
1550e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
1560e6b6b59SJacob Faibussowitsch if (PetscUnlikely(!event_)) PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool<T>().allocate(&event_));
1570e6b6b59SJacob Faibussowitsch PetscFunctionReturn(event_);
1580e6b6b59SJacob Faibussowitsch }
1590e6b6b59SJacob Faibussowitsch
1600e6b6b59SJacob Faibussowitsch template <DeviceType T>
record(cupmStream_t stream)161d71ae5a4SJacob Faibussowitsch inline PetscErrorCode CUPMEvent<T>::record(cupmStream_t stream) noexcept
162d71ae5a4SJacob Faibussowitsch {
1630e6b6b59SJacob Faibussowitsch PetscFunctionBegin;
1640e6b6b59SJacob Faibussowitsch PetscCallCUPM(cupmEventRecord(get(), stream));
1653ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
1660e6b6b59SJacob Faibussowitsch }
1670e6b6b59SJacob Faibussowitsch
1680e6b6b59SJacob Faibussowitsch template <DeviceType T>
operator bool() const169d71ae5a4SJacob Faibussowitsch inline CUPMEvent<T>::operator bool() const noexcept
170d71ae5a4SJacob Faibussowitsch {
1710e6b6b59SJacob Faibussowitsch return event_ != cupmEvent_t{};
1720e6b6b59SJacob Faibussowitsch }
1730e6b6b59SJacob Faibussowitsch
1740e6b6b59SJacob Faibussowitsch } // namespace cupm
1750e6b6b59SJacob Faibussowitsch
1760e6b6b59SJacob Faibussowitsch } // namespace device
1770e6b6b59SJacob Faibussowitsch
1780e6b6b59SJacob Faibussowitsch } // namespace Petsc
179