#pragma once #include #include #include #include namespace Petsc { namespace device { namespace cupm { // A pool for allocating cupmEvent_t's. While events are generally very cheap to create and // destroy, they are not free. Using the pool vs on-demand creation and destruction yields a ~20% // speedup. template class PETSC_SINGLE_LIBRARY_VISIBILITY_INTERNAL CUPMEventPool : impl::Interface, public RegisterFinalizeable> { public: PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T); PetscErrorCode allocate(cupmEvent_t *) noexcept; PetscErrorCode deallocate(cupmEvent_t *) noexcept; PetscErrorCode finalize_() noexcept; private: std::stack pool_; }; template inline PetscErrorCode CUPMEventPool::finalize_() noexcept { PetscFunctionBegin; while (!pool_.empty()) { PetscCallCUPM(cupmEventDestroy(std::move(pool_.top()))); PetscCallCXX(pool_.pop()); } PetscFunctionReturn(PETSC_SUCCESS); } template inline PetscErrorCode CUPMEventPool::allocate(cupmEvent_t *event) noexcept { PetscFunctionBegin; PetscAssertPointer(event, 1); if (pool_.empty()) { PetscCall(this->register_finalize()); PetscCallCUPM(cupmEventCreateWithFlags(event, (unsigned int)flags)); } else { PetscCallCXX(*event = std::move(pool_.top())); PetscCallCXX(pool_.pop()); } PetscFunctionReturn(PETSC_SUCCESS); } template inline PetscErrorCode CUPMEventPool::deallocate(cupmEvent_t *in_event) noexcept { PetscFunctionBegin; PetscAssertPointer(in_event, 1); if (auto event = std::exchange(*in_event, cupmEvent_t{})) { if (this->registered()) { PetscCallCXX(pool_.push(std::move(event))); } else { PetscCallCUPM(cupmEventDestroy(event)); } } PetscFunctionReturn(PETSC_SUCCESS); } template CUPMEventPool &cupm_event_pool() noexcept { static CUPMEventPool pool; return pool; } // pool of events with timing disabled template inline auto cupm_fast_event_pool() noexcept -> decltype(cupm_event_pool::cupmEventDisableTiming>()) & { return cupm_event_pool::cupmEventDisableTiming>(); } // pool of events with timing enabled template inline auto cupm_timer_event_pool() noexcept -> decltype(cupm_event_pool::cupmEventDefault>()) & { return cupm_event_pool::cupmEventDefault>(); } // A simple wrapper of cupmEvent_t. This is used in conjunction with CUPMStream to build the // event-stream pairing for the async allocator. It is also used as the data member of // PetscEvent. template class PETSC_SINGLE_LIBRARY_VISIBILITY_INTERNAL CUPMEvent : impl::Interface, public memory::PoolAllocated { using pool_type = memory::PoolAllocated; public: PETSC_CUPM_INHERIT_INTERFACE_TYPEDEFS_USING(T); constexpr CUPMEvent() noexcept = default; ~CUPMEvent() noexcept; CUPMEvent(CUPMEvent &&) noexcept; CUPMEvent &operator=(CUPMEvent &&) noexcept; // event is not copyable CUPMEvent(const CUPMEvent &) = delete; CUPMEvent &operator=(const CUPMEvent &) = delete; PETSC_NODISCARD cupmEvent_t get() noexcept; PetscErrorCode record(cupmStream_t) noexcept; explicit operator bool() const noexcept; private: cupmEvent_t event_{}; }; template inline CUPMEvent::~CUPMEvent() noexcept { PetscFunctionBegin; PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool().deallocate(&event_)); PetscFunctionReturnVoid(); } template inline CUPMEvent::CUPMEvent(CUPMEvent &&other) noexcept : pool_type(std::move(other)), event_(util::exchange(other.event_, cupmEvent_t{})) { static_assert(std::is_empty>::value, ""); } template inline CUPMEvent &CUPMEvent::operator=(CUPMEvent &&other) noexcept { PetscFunctionBegin; if (this != &other) { pool_type::operator=(std::move(other)); PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool().deallocate(&event_)); event_ = util::exchange(other.event_, cupmEvent_t{}); } PetscFunctionReturn(*this); } template inline typename CUPMEvent::cupmEvent_t CUPMEvent::get() noexcept { PetscFunctionBegin; if (PetscUnlikely(!event_)) PetscCallAbort(PETSC_COMM_SELF, cupm_fast_event_pool().allocate(&event_)); PetscFunctionReturn(event_); } template inline PetscErrorCode CUPMEvent::record(cupmStream_t stream) noexcept { PetscFunctionBegin; PetscCallCUPM(cupmEventRecord(get(), stream)); PetscFunctionReturn(PETSC_SUCCESS); } template inline CUPMEvent::operator bool() const noexcept { return event_ != cupmEvent_t{}; } } // namespace cupm } // namespace device } // namespace Petsc