1*a4963045SJacob Faibussowitsch #pragma once 20e6b6b59SJacob Faibussowitsch 30e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h> 46797ed33SJacob Faibussowitsch 50e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/macros.hpp> 60e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp> 70e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/utility.hpp> 80e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp> 96797ed33SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp> 100e6b6b59SJacob Faibussowitsch 110e6b6b59SJacob Faibussowitsch #include <limits> 120e6b6b59SJacob Faibussowitsch #include <deque> 130e6b6b59SJacob Faibussowitsch #include <vector> 140e6b6b59SJacob Faibussowitsch 15d71ae5a4SJacob Faibussowitsch namespace Petsc 16d71ae5a4SJacob Faibussowitsch { 170e6b6b59SJacob Faibussowitsch 18d71ae5a4SJacob Faibussowitsch namespace device 19d71ae5a4SJacob Faibussowitsch { 200e6b6b59SJacob Faibussowitsch 210e6b6b59SJacob Faibussowitsch template <typename T> 220e6b6b59SJacob Faibussowitsch class StreamBase { 230e6b6b59SJacob Faibussowitsch public: 240e6b6b59SJacob Faibussowitsch using id_type = int; 250e6b6b59SJacob Faibussowitsch using derived_type = T; 260e6b6b59SJacob Faibussowitsch 270e6b6b59SJacob Faibussowitsch static const id_type INVALID_ID; 280e6b6b59SJacob Faibussowitsch 290e6b6b59SJacob Faibussowitsch // needed so that dependent auto works, see veccupmimpl.h for a detailed discussion 300e6b6b59SJacob Faibussowitsch template <typename U = T> 310e6b6b59SJacob Faibussowitsch PETSC_NODISCARD auto get_stream() const noexcept PETSC_DECLTYPE_AUTO_RETURNS(static_cast<const U &>(*this).get_stream_()); 320e6b6b59SJacob Faibussowitsch 330e6b6b59SJacob Faibussowitsch PETSC_NODISCARD id_type get_id() const noexcept { return static_cast<const T &>(*this).get_id_(); } 340e6b6b59SJacob Faibussowitsch 350e6b6b59SJacob Faibussowitsch template <typename E> 36089fb57cSJacob Faibussowitsch PetscErrorCode record_event(E &&event) const noexcept 37d71ae5a4SJacob Faibussowitsch { 380e6b6b59SJacob Faibussowitsch return static_cast<const T &>(*this).record_event_(std::forward<E>(event)); 390e6b6b59SJacob Faibussowitsch } 400e6b6b59SJacob Faibussowitsch 410e6b6b59SJacob Faibussowitsch template <typename E> 42089fb57cSJacob Faibussowitsch PetscErrorCode wait_for_event(E &&event) const noexcept 43d71ae5a4SJacob Faibussowitsch { 440e6b6b59SJacob Faibussowitsch return static_cast<const T &>(*this).wait_for_(std::forward<E>(event)); 450e6b6b59SJacob Faibussowitsch } 460e6b6b59SJacob Faibussowitsch 470e6b6b59SJacob Faibussowitsch protected: 480e6b6b59SJacob Faibussowitsch constexpr StreamBase() noexcept = default; 490e6b6b59SJacob Faibussowitsch 500e6b6b59SJacob Faibussowitsch struct default_event_type { }; 510e6b6b59SJacob Faibussowitsch using default_stream_type = std::nullptr_t; 520e6b6b59SJacob Faibussowitsch 530e6b6b59SJacob Faibussowitsch PETSC_NODISCARD static constexpr default_stream_type get_stream_() noexcept { return nullptr; } 540e6b6b59SJacob Faibussowitsch 550e6b6b59SJacob Faibussowitsch PETSC_NODISCARD static constexpr id_type get_id_() noexcept { return 0; } 560e6b6b59SJacob Faibussowitsch 570e6b6b59SJacob Faibussowitsch template <typename U = T> 58089fb57cSJacob Faibussowitsch static constexpr PetscErrorCode record_event_(const typename U::event_type &) noexcept 59d71ae5a4SJacob Faibussowitsch { 603ba16761SJacob Faibussowitsch return PETSC_SUCCESS; 610e6b6b59SJacob Faibussowitsch } 620e6b6b59SJacob Faibussowitsch 630e6b6b59SJacob Faibussowitsch template <typename U = T> 64089fb57cSJacob Faibussowitsch static constexpr PetscErrorCode wait_for_(const typename U::event_type &) noexcept 65d71ae5a4SJacob Faibussowitsch { 663ba16761SJacob Faibussowitsch return PETSC_SUCCESS; 670e6b6b59SJacob Faibussowitsch } 680e6b6b59SJacob Faibussowitsch }; 690e6b6b59SJacob Faibussowitsch 700e6b6b59SJacob Faibussowitsch template <typename T> 710e6b6b59SJacob Faibussowitsch const typename StreamBase<T>::id_type StreamBase<T>::INVALID_ID = -1; 720e6b6b59SJacob Faibussowitsch 730e6b6b59SJacob Faibussowitsch struct DefaultStream : StreamBase<DefaultStream> { 740e6b6b59SJacob Faibussowitsch using stream_type = typename StreamBase<DefaultStream>::default_stream_type; 750e6b6b59SJacob Faibussowitsch using id_type = typename StreamBase<DefaultStream>::id_type; 760e6b6b59SJacob Faibussowitsch using event_type = typename StreamBase<DefaultStream>::default_event_type; 770e6b6b59SJacob Faibussowitsch }; 780e6b6b59SJacob Faibussowitsch 790e6b6b59SJacob Faibussowitsch } // namespace device 800e6b6b59SJacob Faibussowitsch 81d71ae5a4SJacob Faibussowitsch namespace memory 82d71ae5a4SJacob Faibussowitsch { 830e6b6b59SJacob Faibussowitsch 84d71ae5a4SJacob Faibussowitsch namespace impl 85d71ae5a4SJacob Faibussowitsch { 860e6b6b59SJacob Faibussowitsch 870e6b6b59SJacob Faibussowitsch // ========================================================================================== 880e6b6b59SJacob Faibussowitsch // MemoryChunk 890e6b6b59SJacob Faibussowitsch // 900e6b6b59SJacob Faibussowitsch // Represents a checked-out region of a MemoryBlock. Tracks the offset into the owning 910e6b6b59SJacob Faibussowitsch // MemoryBlock and its size/capacity 920e6b6b59SJacob Faibussowitsch // ========================================================================================== 930e6b6b59SJacob Faibussowitsch 940e6b6b59SJacob Faibussowitsch template <typename EventType> 950e6b6b59SJacob Faibussowitsch class MemoryChunk { 960e6b6b59SJacob Faibussowitsch public: 970e6b6b59SJacob Faibussowitsch using event_type = EventType; 980e6b6b59SJacob Faibussowitsch using size_type = std::size_t; 990e6b6b59SJacob Faibussowitsch 1000e6b6b59SJacob Faibussowitsch MemoryChunk(size_type, size_type) noexcept; 1010e6b6b59SJacob Faibussowitsch explicit MemoryChunk(size_type) noexcept; 1020e6b6b59SJacob Faibussowitsch 1030e6b6b59SJacob Faibussowitsch MemoryChunk(MemoryChunk &&) noexcept; 1040e6b6b59SJacob Faibussowitsch MemoryChunk &operator=(MemoryChunk &&) noexcept; 1050e6b6b59SJacob Faibussowitsch 1060e6b6b59SJacob Faibussowitsch MemoryChunk(const MemoryChunk &) noexcept = delete; 1070e6b6b59SJacob Faibussowitsch MemoryChunk &operator=(const MemoryChunk &) noexcept = delete; 1080e6b6b59SJacob Faibussowitsch 1090e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type start() const noexcept { return start_; } 1100e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type size() const noexcept { return size_; } 1110e6b6b59SJacob Faibussowitsch // REVIEW ME: 1120e6b6b59SJacob Faibussowitsch // make this an actual field, normally each chunk shrinks_to_fit() on begin claimed, but in 1130e6b6b59SJacob Faibussowitsch // theory only the last chunk needs to do this 1140e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type capacity() const noexcept { return size_; } 1150e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type total_offset() const noexcept { return start() + size(); } 1160e6b6b59SJacob Faibussowitsch 1170e6b6b59SJacob Faibussowitsch template <typename U> 118089fb57cSJacob Faibussowitsch PetscErrorCode release(const device::StreamBase<U> *) noexcept; 1190e6b6b59SJacob Faibussowitsch template <typename U> 120089fb57cSJacob Faibussowitsch PetscErrorCode claim(const device::StreamBase<U> *, size_type, bool *, bool = false) noexcept; 1210e6b6b59SJacob Faibussowitsch template <typename U> 1220e6b6b59SJacob Faibussowitsch PETSC_NODISCARD bool can_claim(const device::StreamBase<U> *, size_type, bool) const noexcept; 123089fb57cSJacob Faibussowitsch PetscErrorCode resize(size_type) noexcept; 1246797ed33SJacob Faibussowitsch PETSC_NODISCARD bool contains(size_type) const noexcept; 1250e6b6b59SJacob Faibussowitsch 1260e6b6b59SJacob Faibussowitsch private: 1270e6b6b59SJacob Faibussowitsch // clang-format off 1280e6b6b59SJacob Faibussowitsch event_type event_{}; // event recorded when the chunk was released 1290e6b6b59SJacob Faibussowitsch bool open_ = true; // is this chunk open? 1300e6b6b59SJacob Faibussowitsch // id of the last stream to use the chunk, populated on release 1310e6b6b59SJacob Faibussowitsch int stream_id_ = device::DefaultStream::INVALID_ID; 1320e6b6b59SJacob Faibussowitsch size_type size_ = 0; // size of the chunk 1330e6b6b59SJacob Faibussowitsch const size_type start_ = 0; // offset from the start of the owning block 1340e6b6b59SJacob Faibussowitsch // clang-format on 1356797ed33SJacob Faibussowitsch 1360e6b6b59SJacob Faibussowitsch template <typename U> 1370e6b6b59SJacob Faibussowitsch PETSC_NODISCARD bool stream_compat_(const device::StreamBase<U> *) const noexcept; 1380e6b6b59SJacob Faibussowitsch }; 1390e6b6b59SJacob Faibussowitsch 1400e6b6b59SJacob Faibussowitsch // ========================================================================================== 1410e6b6b59SJacob Faibussowitsch // MemoryChunk - Private API 1420e6b6b59SJacob Faibussowitsch // ========================================================================================== 1430e6b6b59SJacob Faibussowitsch 1440e6b6b59SJacob Faibussowitsch // asks and answers the question: can this stream claim this chunk without serializing? 1450e6b6b59SJacob Faibussowitsch template <typename E> 1460e6b6b59SJacob Faibussowitsch template <typename U> 147d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::stream_compat_(const device::StreamBase<U> *strm) const noexcept 148d71ae5a4SJacob Faibussowitsch { 1490e6b6b59SJacob Faibussowitsch return (stream_id_ == strm->INVALID_ID) || (stream_id_ == strm->get_id()); 1500e6b6b59SJacob Faibussowitsch } 1510e6b6b59SJacob Faibussowitsch 1520e6b6b59SJacob Faibussowitsch // ========================================================================================== 1530e6b6b59SJacob Faibussowitsch // MemoryChunk - Public API 1540e6b6b59SJacob Faibussowitsch // ========================================================================================== 1550e6b6b59SJacob Faibussowitsch 1560e6b6b59SJacob Faibussowitsch template <typename E> 157d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type start, size_type size) noexcept : size_(size), start_(start) 158d71ae5a4SJacob Faibussowitsch { 159d71ae5a4SJacob Faibussowitsch } 1600e6b6b59SJacob Faibussowitsch 1610e6b6b59SJacob Faibussowitsch template <typename E> 162d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type size) noexcept : MemoryChunk(0, size) 163d71ae5a4SJacob Faibussowitsch { 164d71ae5a4SJacob Faibussowitsch } 1650e6b6b59SJacob Faibussowitsch 1660e6b6b59SJacob Faibussowitsch template <typename E> 1670e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(MemoryChunk<E> &&other) noexcept : 168d71ae5a4SJacob Faibussowitsch event_(std::move(other.event_)), open_(util::exchange(other.open_, false)), stream_id_(util::exchange(other.stream_id_, device::DefaultStream::INVALID_ID)), size_(util::exchange(other.size_, 0)), start_(std::move(other.start_)) 169d71ae5a4SJacob Faibussowitsch { 170d71ae5a4SJacob Faibussowitsch } 1710e6b6b59SJacob Faibussowitsch 1720e6b6b59SJacob Faibussowitsch template <typename E> 173d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E> &MemoryChunk<E>::operator=(MemoryChunk<E> &&other) noexcept 174d71ae5a4SJacob Faibussowitsch { 1750e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1760e6b6b59SJacob Faibussowitsch if (this != &other) { 1770e6b6b59SJacob Faibussowitsch event_ = std::move(other.event_); 1780e6b6b59SJacob Faibussowitsch open_ = util::exchange(other.open_, false); 1790e6b6b59SJacob Faibussowitsch stream_id_ = util::exchange(other.stream_id_, device::DefaultStream::INVALID_ID); 1800e6b6b59SJacob Faibussowitsch size_ = util::exchange(other.size_, 0); 1810e6b6b59SJacob Faibussowitsch start_ = std::move(other.start_); 1820e6b6b59SJacob Faibussowitsch } 1830e6b6b59SJacob Faibussowitsch PetscFunctionReturn(*this); 1840e6b6b59SJacob Faibussowitsch } 1850e6b6b59SJacob Faibussowitsch 1860e6b6b59SJacob Faibussowitsch /* 1870e6b6b59SJacob Faibussowitsch MemoryChunk::release - release a chunk on a stream 1880e6b6b59SJacob Faibussowitsch 1890e6b6b59SJacob Faibussowitsch Input Parameter: 1900e6b6b59SJacob Faibussowitsch . stream - the stream to release the chunk with 1910e6b6b59SJacob Faibussowitsch 1920e6b6b59SJacob Faibussowitsch Notes: 1930e6b6b59SJacob Faibussowitsch Inserts a release operation on stream and records the state of stream at the time this 1940e6b6b59SJacob Faibussowitsch routine was called. 1950e6b6b59SJacob Faibussowitsch 1960e6b6b59SJacob Faibussowitsch Future allocation requests which attempt to claim the chunk on the same stream may re-acquire 1970e6b6b59SJacob Faibussowitsch the chunk without serialization. 1980e6b6b59SJacob Faibussowitsch 1990e6b6b59SJacob Faibussowitsch If another stream attempts to claim the chunk they must wait for the recorded event before 2000e6b6b59SJacob Faibussowitsch claiming the chunk. 2010e6b6b59SJacob Faibussowitsch */ 2020e6b6b59SJacob Faibussowitsch template <typename E> 2030e6b6b59SJacob Faibussowitsch template <typename U> 204d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::release(const device::StreamBase<U> *stream) noexcept 205d71ae5a4SJacob Faibussowitsch { 2060e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2070e6b6b59SJacob Faibussowitsch open_ = true; 2080e6b6b59SJacob Faibussowitsch stream_id_ = stream->get_id(); 2090e6b6b59SJacob Faibussowitsch PetscCall(stream->record_event(event_)); 2103ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2110e6b6b59SJacob Faibussowitsch } 2120e6b6b59SJacob Faibussowitsch 2130e6b6b59SJacob Faibussowitsch /* 2140e6b6b59SJacob Faibussowitsch MemoryChunk::claim - attempt to claim a particular chunk 2150e6b6b59SJacob Faibussowitsch 2160e6b6b59SJacob Faibussowitsch Input Parameters: 2170e6b6b59SJacob Faibussowitsch + stream - the stream on which to attempt to claim 2180e6b6b59SJacob Faibussowitsch . req_size - the requested size (in elements) to attempt to claim 2190e6b6b59SJacob Faibussowitsch - serialize - (optional, false) whether the claimant allows serialization 2200e6b6b59SJacob Faibussowitsch 2210e6b6b59SJacob Faibussowitsch Output Parameter: 2220e6b6b59SJacob Faibussowitsch . success - true if the chunk was claimed, false otherwise 2230e6b6b59SJacob Faibussowitsch */ 2240e6b6b59SJacob Faibussowitsch template <typename E> 2250e6b6b59SJacob Faibussowitsch template <typename U> 226d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::claim(const device::StreamBase<U> *stream, size_type req_size, bool *success, bool serialize) noexcept 227d71ae5a4SJacob Faibussowitsch { 2280e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2290e6b6b59SJacob Faibussowitsch if ((*success = can_claim(stream, req_size, serialize))) { 2300e6b6b59SJacob Faibussowitsch if (serialize && !stream_compat_(stream)) PetscCall(stream->wait_for_event(event_)); 2310e6b6b59SJacob Faibussowitsch PetscCall(resize(req_size)); 2320e6b6b59SJacob Faibussowitsch open_ = false; 2330e6b6b59SJacob Faibussowitsch } 2343ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2350e6b6b59SJacob Faibussowitsch } 2360e6b6b59SJacob Faibussowitsch 2370e6b6b59SJacob Faibussowitsch /* 2380e6b6b59SJacob Faibussowitsch MemoryChunk::can_claim - test whether a particular chunk can be claimed 2390e6b6b59SJacob Faibussowitsch 2400e6b6b59SJacob Faibussowitsch Input Parameters: 2410e6b6b59SJacob Faibussowitsch + stream - the stream on which to attempt to claim 2420e6b6b59SJacob Faibussowitsch . req_size - the requested size (in elements) to attempt to claim 2430e6b6b59SJacob Faibussowitsch - serialize - whether the claimant allows serialization 2440e6b6b59SJacob Faibussowitsch 2450e6b6b59SJacob Faibussowitsch Output: 2460e6b6b59SJacob Faibussowitsch . [return] - true if the chunk is claimable given the configuration, false otherwise 2470e6b6b59SJacob Faibussowitsch */ 2480e6b6b59SJacob Faibussowitsch template <typename E> 2490e6b6b59SJacob Faibussowitsch template <typename U> 250d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::can_claim(const device::StreamBase<U> *stream, size_type req_size, bool serialize) const noexcept 251d71ae5a4SJacob Faibussowitsch { 2520e6b6b59SJacob Faibussowitsch if (open_ && (req_size <= capacity())) { 2530e6b6b59SJacob Faibussowitsch // fully compatible 2540e6b6b59SJacob Faibussowitsch if (stream_compat_(stream)) return true; 2550e6b6b59SJacob Faibussowitsch // stream wasn't compatible, but could claim if we serialized 2560e6b6b59SJacob Faibussowitsch if (serialize) return true; 2570e6b6b59SJacob Faibussowitsch // incompatible stream and did not want to serialize 2580e6b6b59SJacob Faibussowitsch } 2590e6b6b59SJacob Faibussowitsch return false; 2600e6b6b59SJacob Faibussowitsch } 2610e6b6b59SJacob Faibussowitsch 2620e6b6b59SJacob Faibussowitsch /* 2630e6b6b59SJacob Faibussowitsch MemoryChunk::resize - grow a chunk to new size 2640e6b6b59SJacob Faibussowitsch 2650e6b6b59SJacob Faibussowitsch Input Parameter: 2660e6b6b59SJacob Faibussowitsch . newsize - the new size Requested 2670e6b6b59SJacob Faibussowitsch 2680e6b6b59SJacob Faibussowitsch Notes: 2690e6b6b59SJacob Faibussowitsch newsize cannot be larger than capacity 2700e6b6b59SJacob Faibussowitsch */ 2710e6b6b59SJacob Faibussowitsch template <typename E> 272d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::resize(size_type newsize) noexcept 273d71ae5a4SJacob Faibussowitsch { 2740e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2750e6b6b59SJacob Faibussowitsch PetscAssert(newsize <= capacity(), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "New size %zu larger than capacity %zu", newsize, capacity()); 2760e6b6b59SJacob Faibussowitsch size_ = newsize; 2773ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2780e6b6b59SJacob Faibussowitsch } 2790e6b6b59SJacob Faibussowitsch 2806797ed33SJacob Faibussowitsch /* 2816797ed33SJacob Faibussowitsch MemoryChunk::contains - query whether a memory chunk contains a particular offset 2826797ed33SJacob Faibussowitsch 2836797ed33SJacob Faibussowitsch Input Parameters: 2846797ed33SJacob Faibussowitsch . offset - The offset from the MemoryBlock start 2856797ed33SJacob Faibussowitsch 2866797ed33SJacob Faibussowitsch Notes: 2876797ed33SJacob Faibussowitsch Returns true if the chunk contains the offset, false otherwise 2886797ed33SJacob Faibussowitsch */ 2896797ed33SJacob Faibussowitsch template <typename E> 290d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::contains(size_type offset) const noexcept 291d71ae5a4SJacob Faibussowitsch { 2926797ed33SJacob Faibussowitsch return (offset >= start()) && (offset < total_offset()); 2936797ed33SJacob Faibussowitsch } 2946797ed33SJacob Faibussowitsch 2950e6b6b59SJacob Faibussowitsch // ========================================================================================== 2960e6b6b59SJacob Faibussowitsch // MemoryBlock 2970e6b6b59SJacob Faibussowitsch // 2980e6b6b59SJacob Faibussowitsch // A "memory block" manager, which owns the pointer to a particular memory range. Retrieving 2990e6b6b59SJacob Faibussowitsch // and restoring a block is thread-safe (so may be used by multiple device streams). 3000e6b6b59SJacob Faibussowitsch // ========================================================================================== 3010e6b6b59SJacob Faibussowitsch 3020e6b6b59SJacob Faibussowitsch template <typename T, typename AllocatorType, typename StreamType> 3030e6b6b59SJacob Faibussowitsch class MemoryBlock { 3040e6b6b59SJacob Faibussowitsch public: 3050e6b6b59SJacob Faibussowitsch using value_type = T; 3060e6b6b59SJacob Faibussowitsch using allocator_type = AllocatorType; 3070e6b6b59SJacob Faibussowitsch using stream_type = StreamType; 3080e6b6b59SJacob Faibussowitsch using event_type = typename stream_type::event_type; 3090e6b6b59SJacob Faibussowitsch using chunk_type = MemoryChunk<event_type>; 3100e6b6b59SJacob Faibussowitsch using size_type = typename chunk_type::size_type; 3110e6b6b59SJacob Faibussowitsch using chunk_list_type = std::vector<chunk_type>; 3120e6b6b59SJacob Faibussowitsch 3130e6b6b59SJacob Faibussowitsch template <typename U> 3140e6b6b59SJacob Faibussowitsch MemoryBlock(allocator_type *, size_type, const device::StreamBase<U> *) noexcept; 3150e6b6b59SJacob Faibussowitsch 3160e6b6b59SJacob Faibussowitsch ~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value); 3170e6b6b59SJacob Faibussowitsch 3180e6b6b59SJacob Faibussowitsch MemoryBlock(MemoryBlock &&) noexcept; 3190e6b6b59SJacob Faibussowitsch MemoryBlock &operator=(MemoryBlock &&) noexcept; 3200e6b6b59SJacob Faibussowitsch 3210e6b6b59SJacob Faibussowitsch // memory blocks are not copyable 3220e6b6b59SJacob Faibussowitsch MemoryBlock(const MemoryBlock &) = delete; 3230e6b6b59SJacob Faibussowitsch MemoryBlock &operator=(const MemoryBlock &) = delete; 3240e6b6b59SJacob Faibussowitsch 3250e6b6b59SJacob Faibussowitsch /* --- actual functions --- */ 326089fb57cSJacob Faibussowitsch PetscErrorCode try_allocate_chunk(size_type, T **, const stream_type *, bool *) noexcept; 327089fb57cSJacob Faibussowitsch PetscErrorCode try_deallocate_chunk(T **, const stream_type *, bool *) noexcept; 328089fb57cSJacob Faibussowitsch PetscErrorCode try_find_chunk(const T *, chunk_type **) noexcept; 3290e6b6b59SJacob Faibussowitsch PETSC_NODISCARD bool owns_pointer(const T *) const noexcept; 3300e6b6b59SJacob Faibussowitsch 3310e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type size() const noexcept { return size_; } 3320e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type bytes() const noexcept { return sizeof(value_type) * size(); } 3330e6b6b59SJacob Faibussowitsch PETSC_NODISCARD size_type num_chunks() const noexcept { return chunks_.size(); } 3340e6b6b59SJacob Faibussowitsch 3350e6b6b59SJacob Faibussowitsch private: 3360e6b6b59SJacob Faibussowitsch value_type *mem_{}; 3370e6b6b59SJacob Faibussowitsch allocator_type *allocator_{}; 3380e6b6b59SJacob Faibussowitsch size_type size_{}; 3390e6b6b59SJacob Faibussowitsch chunk_list_type chunks_{}; 3400e6b6b59SJacob Faibussowitsch 341089fb57cSJacob Faibussowitsch PetscErrorCode clear_(const stream_type *) noexcept; 3420e6b6b59SJacob Faibussowitsch }; 3430e6b6b59SJacob Faibussowitsch 3440e6b6b59SJacob Faibussowitsch // ========================================================================================== 3450e6b6b59SJacob Faibussowitsch // MemoryBlock - Private API 3460e6b6b59SJacob Faibussowitsch // ========================================================================================== 3470e6b6b59SJacob Faibussowitsch 3480e6b6b59SJacob Faibussowitsch // clear the memory block, called from destructors and move assignment/construction 3490e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 350089fb57cSJacob Faibussowitsch PetscErrorCode MemoryBlock<T, A, S>::clear_(const stream_type *stream) noexcept 351d71ae5a4SJacob Faibussowitsch { 3520e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3530e6b6b59SJacob Faibussowitsch if (PetscLikely(mem_)) { 3540e6b6b59SJacob Faibussowitsch PetscCall(allocator_->deallocate(mem_, stream)); 3550e6b6b59SJacob Faibussowitsch mem_ = nullptr; 3560e6b6b59SJacob Faibussowitsch } 3570e6b6b59SJacob Faibussowitsch size_ = 0; 3580e6b6b59SJacob Faibussowitsch PetscCallCXX(chunks_.clear()); 3593ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 3600e6b6b59SJacob Faibussowitsch } 3610e6b6b59SJacob Faibussowitsch 3620e6b6b59SJacob Faibussowitsch // ========================================================================================== 3630e6b6b59SJacob Faibussowitsch // MemoryBlock - Public API 3640e6b6b59SJacob Faibussowitsch // ========================================================================================== 3650e6b6b59SJacob Faibussowitsch 3660e6b6b59SJacob Faibussowitsch // default constructor, allocates memory immediately 3670e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 3680e6b6b59SJacob Faibussowitsch template <typename U> 369d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::MemoryBlock(allocator_type *alloc, size_type s, const device::StreamBase<U> *stream) noexcept : allocator_(alloc), size_(s) 370d71ae5a4SJacob Faibussowitsch { 3710e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3720e6b6b59SJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, alloc->allocate(&mem_, s, stream)); 3730e6b6b59SJacob Faibussowitsch PetscAssertAbort(mem_, PETSC_COMM_SELF, PETSC_ERR_MEM, "Failed to allocate memory block of size %zu", s); 3740e6b6b59SJacob Faibussowitsch PetscFunctionReturnVoid(); 3750e6b6b59SJacob Faibussowitsch } 3760e6b6b59SJacob Faibussowitsch 3770e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 378d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value) 379d71ae5a4SJacob Faibussowitsch { 3800e6b6b59SJacob Faibussowitsch stream_type stream; 3810e6b6b59SJacob Faibussowitsch 3820e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3830e6b6b59SJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, clear_(&stream)); 3840e6b6b59SJacob Faibussowitsch PetscFunctionReturnVoid(); 3850e6b6b59SJacob Faibussowitsch } 3860e6b6b59SJacob Faibussowitsch 3870e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 388d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::MemoryBlock(MemoryBlock &&other) noexcept : mem_(util::exchange(other.mem_, nullptr)), allocator_(other.allocator_), size_(util::exchange(other.size_, 0)), chunks_(std::move(other.chunks_)) 389d71ae5a4SJacob Faibussowitsch { 390d71ae5a4SJacob Faibussowitsch } 3910e6b6b59SJacob Faibussowitsch 3920e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 393d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S> &MemoryBlock<T, A, S>::operator=(MemoryBlock &&other) noexcept 394d71ae5a4SJacob Faibussowitsch { 3950e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 3960e6b6b59SJacob Faibussowitsch if (this != &other) { 3970e6b6b59SJacob Faibussowitsch stream_type stream; 3980e6b6b59SJacob Faibussowitsch 3990e6b6b59SJacob Faibussowitsch PetscCallAbort(PETSC_COMM_SELF, clear_(&stream)); 4000e6b6b59SJacob Faibussowitsch mem_ = util::exchange(other.mem_, nullptr); 4010e6b6b59SJacob Faibussowitsch allocator_ = other.allocator_; 4020e6b6b59SJacob Faibussowitsch size_ = util::exchange(other.size_, 0); 4030e6b6b59SJacob Faibussowitsch chunks_ = std::move(other.chunks_); 4040e6b6b59SJacob Faibussowitsch } 4050e6b6b59SJacob Faibussowitsch PetscFunctionReturn(*this); 4060e6b6b59SJacob Faibussowitsch } 4070e6b6b59SJacob Faibussowitsch 4080e6b6b59SJacob Faibussowitsch /* 4090e6b6b59SJacob Faibussowitsch MemoryBock::owns_pointer - returns true if this block owns a pointer, false otherwise 4100e6b6b59SJacob Faibussowitsch */ 4110e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 412d71ae5a4SJacob Faibussowitsch inline bool MemoryBlock<T, A, S>::owns_pointer(const T *ptr) const noexcept 413d71ae5a4SJacob Faibussowitsch { 4140e6b6b59SJacob Faibussowitsch // each pool is linear in memory, so it suffices to check the bounds 4150e6b6b59SJacob Faibussowitsch return (ptr >= mem_) && (ptr < std::next(mem_, size())); 4160e6b6b59SJacob Faibussowitsch } 4170e6b6b59SJacob Faibussowitsch 4180e6b6b59SJacob Faibussowitsch /* 4190e6b6b59SJacob Faibussowitsch MemoryBlock::try_allocate_chunk - try to get a chunk from this MemoryBlock 4200e6b6b59SJacob Faibussowitsch 4210e6b6b59SJacob Faibussowitsch Input Parameters: 4220e6b6b59SJacob Faibussowitsch + req_size - the requested size of the allocation (in elements) 4230e6b6b59SJacob Faibussowitsch . ptr - ptr to fill 4240e6b6b59SJacob Faibussowitsch - stream - stream to fill the pointer on 4250e6b6b59SJacob Faibussowitsch 4260e6b6b59SJacob Faibussowitsch Output Parameter: 4270e6b6b59SJacob Faibussowitsch . success - true if chunk was gotten, false otherwise 4280e6b6b59SJacob Faibussowitsch 4290e6b6b59SJacob Faibussowitsch Notes: 4300e6b6b59SJacob Faibussowitsch If the current memory could not satisfy the memory request, ptr is unchanged 4310e6b6b59SJacob Faibussowitsch */ 4320e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 433d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_allocate_chunk(size_type req_size, T **ptr, const stream_type *stream, bool *success) noexcept 434d71ae5a4SJacob Faibussowitsch { 4350e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 4360e6b6b59SJacob Faibussowitsch *success = false; 4370e6b6b59SJacob Faibussowitsch if (req_size <= size()) { 4380e6b6b59SJacob Faibussowitsch const auto try_create_chunk = [&]() { 4390e6b6b59SJacob Faibussowitsch const auto was_empty = chunks_.empty(); 4400e6b6b59SJacob Faibussowitsch const auto block_alloced = was_empty ? 0 : chunks_.back().total_offset(); 4410e6b6b59SJacob Faibussowitsch 4420e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 4430e6b6b59SJacob Faibussowitsch if (block_alloced + req_size <= size()) { 4440e6b6b59SJacob Faibussowitsch PetscCallCXX(chunks_.emplace_back(block_alloced, req_size)); 4450e6b6b59SJacob Faibussowitsch PetscCall(chunks_.back().claim(stream, req_size, success)); 4460e6b6b59SJacob Faibussowitsch *ptr = mem_ + block_alloced; 4470e6b6b59SJacob Faibussowitsch if (was_empty) PetscAssert(*success, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to claim chunk (of size %zu) even though block (of size %zu) was empty!", req_size, size()); 4480e6b6b59SJacob Faibussowitsch } 4493ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 4500e6b6b59SJacob Faibussowitsch }; 4510e6b6b59SJacob Faibussowitsch const auto try_find_open_chunk = [&](bool serialize = false) { 4520e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 4530e6b6b59SJacob Faibussowitsch for (auto &chunk : chunks_) { 4540e6b6b59SJacob Faibussowitsch PetscCall(chunk.claim(stream, req_size, success, serialize)); 4550e6b6b59SJacob Faibussowitsch if (*success) { 4560e6b6b59SJacob Faibussowitsch *ptr = mem_ + chunk.start(); 4570e6b6b59SJacob Faibussowitsch break; 4580e6b6b59SJacob Faibussowitsch } 4590e6b6b59SJacob Faibussowitsch } 4603ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 4610e6b6b59SJacob Faibussowitsch }; 4626797ed33SJacob Faibussowitsch const auto try_steal_other_stream_chunk = [&]() { 4636797ed33SJacob Faibussowitsch PetscFunctionBegin; 4646797ed33SJacob Faibussowitsch PetscCall(try_find_open_chunk(true)); 4653ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 4666797ed33SJacob Faibussowitsch }; 4670e6b6b59SJacob Faibussowitsch 4680e6b6b59SJacob Faibussowitsch // search previously distributed chunks, but only claim one if it is on the same stream 4690e6b6b59SJacob Faibussowitsch // as us 4700e6b6b59SJacob Faibussowitsch PetscCall(try_find_open_chunk()); 4710e6b6b59SJacob Faibussowitsch 4720e6b6b59SJacob Faibussowitsch // if we are here we couldn't reuse one of our own chunks so check first if the pool 4730e6b6b59SJacob Faibussowitsch // has room for a new one 4740e6b6b59SJacob Faibussowitsch if (!*success) PetscCall(try_create_chunk()); 4750e6b6b59SJacob Faibussowitsch 4760e6b6b59SJacob Faibussowitsch // try pruning dead chunks off the back, note we do this regardless of whether we are 4770e6b6b59SJacob Faibussowitsch // successful 4780e6b6b59SJacob Faibussowitsch while (chunks_.back().can_claim(stream, 0, false)) { 4790e6b6b59SJacob Faibussowitsch PetscCallCXX(chunks_.pop_back()); 4800e6b6b59SJacob Faibussowitsch if (chunks_.empty()) { 4810e6b6b59SJacob Faibussowitsch // if chunks are empty it implies we have managed to claim (and subsequently destroy) 4820e6b6b59SJacob Faibussowitsch // our own chunk twice! something has gone wrong 4830e6b6b59SJacob Faibussowitsch PetscAssert(!*success, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Successfully claimed a chunk (of size %zu, from block of size %zu) but have now managed to claim it for a second time (and destroyed it)!", req_size, size()); 4840e6b6b59SJacob Faibussowitsch break; 4850e6b6b59SJacob Faibussowitsch } 4860e6b6b59SJacob Faibussowitsch } 4870e6b6b59SJacob Faibussowitsch 4880e6b6b59SJacob Faibussowitsch // if previously unsuccessful see if enough space has opened up due to pruning. note that 4890e6b6b59SJacob Faibussowitsch // if the chunk list was emptied from the pruning this call must succeed in allocating a 4900e6b6b59SJacob Faibussowitsch // chunk, otherwise something is wrong 4910e6b6b59SJacob Faibussowitsch if (!*success) PetscCall(try_create_chunk()); 4920e6b6b59SJacob Faibussowitsch 4930e6b6b59SJacob Faibussowitsch // last resort, iterate over all chunks and see if we can steal one by waiting on the 4940e6b6b59SJacob Faibussowitsch // current owner to finish using it 4956797ed33SJacob Faibussowitsch if (!*success) PetscCall(try_steal_other_stream_chunk()); 4960e6b6b59SJacob Faibussowitsch } 4973ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 4980e6b6b59SJacob Faibussowitsch } 4990e6b6b59SJacob Faibussowitsch 5000e6b6b59SJacob Faibussowitsch /* 5010e6b6b59SJacob Faibussowitsch MemoryBlock::try_deallocate_chunk - try to restore a chunk to this MemoryBlock 5020e6b6b59SJacob Faibussowitsch 5030e6b6b59SJacob Faibussowitsch Input Parameters: 5040e6b6b59SJacob Faibussowitsch + ptr - ptr to restore 5050e6b6b59SJacob Faibussowitsch - stream - stream to restore the pointer on 5060e6b6b59SJacob Faibussowitsch 5070e6b6b59SJacob Faibussowitsch Output Parameter: 5080e6b6b59SJacob Faibussowitsch . success - true if chunk was restored, false otherwise 5090e6b6b59SJacob Faibussowitsch 5100e6b6b59SJacob Faibussowitsch Notes: 5110e6b6b59SJacob Faibussowitsch ptr is set to nullptr on successful restore, and is unchanged otherwise. If the ptr is owned 51235cb6cd3SPierre Jolivet by this MemoryBlock then it is restored on stream. The same stream may receive ptr again 5130e6b6b59SJacob Faibussowitsch without synchronization, but other streams may not do so until either serializing or the 5140e6b6b59SJacob Faibussowitsch stream is idle again. 5150e6b6b59SJacob Faibussowitsch */ 5160e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 517d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_deallocate_chunk(T **ptr, const stream_type *stream, bool *success) noexcept 518d71ae5a4SJacob Faibussowitsch { 5190e6b6b59SJacob Faibussowitsch chunk_type *chunk = nullptr; 5200e6b6b59SJacob Faibussowitsch 5210e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 5220e6b6b59SJacob Faibussowitsch PetscCall(try_find_chunk(*ptr, &chunk)); 5230e6b6b59SJacob Faibussowitsch if (chunk) { 5240e6b6b59SJacob Faibussowitsch PetscCall(chunk->release(stream)); 5250e6b6b59SJacob Faibussowitsch *ptr = nullptr; 5260e6b6b59SJacob Faibussowitsch *success = true; 5270e6b6b59SJacob Faibussowitsch } else { 5280e6b6b59SJacob Faibussowitsch *success = false; 5290e6b6b59SJacob Faibussowitsch } 5303ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 5310e6b6b59SJacob Faibussowitsch } 5320e6b6b59SJacob Faibussowitsch 5330e6b6b59SJacob Faibussowitsch /* 5340e6b6b59SJacob Faibussowitsch MemoryBlock::try_find_chunk - try to find the chunk which owns ptr 5350e6b6b59SJacob Faibussowitsch 5360e6b6b59SJacob Faibussowitsch Input Parameter: 537146a86ebSJacob Faibussowitsch . ptr - the pointer to look for 5380e6b6b59SJacob Faibussowitsch 5390e6b6b59SJacob Faibussowitsch Output Parameter: 5400e6b6b59SJacob Faibussowitsch . ret_chunk - pointer to the owning chunk or nullptr if not found 5410e6b6b59SJacob Faibussowitsch */ 5420e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S> 543d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_find_chunk(const T *ptr, chunk_type **ret_chunk) noexcept 544d71ae5a4SJacob Faibussowitsch { 5450e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 5460e6b6b59SJacob Faibussowitsch *ret_chunk = nullptr; 5470e6b6b59SJacob Faibussowitsch if (owns_pointer(ptr)) { 5480e6b6b59SJacob Faibussowitsch const auto offset = static_cast<size_type>(ptr - mem_); 5490e6b6b59SJacob Faibussowitsch 5500e6b6b59SJacob Faibussowitsch for (auto &chunk : chunks_) { 5516797ed33SJacob Faibussowitsch if (chunk.contains(offset)) { 5520e6b6b59SJacob Faibussowitsch *ret_chunk = &chunk; 5530e6b6b59SJacob Faibussowitsch break; 5540e6b6b59SJacob Faibussowitsch } 5550e6b6b59SJacob Faibussowitsch } 5560e6b6b59SJacob Faibussowitsch 5570e6b6b59SJacob Faibussowitsch PetscAssert(*ret_chunk, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to find %zu in block, even though it is within block range [%zu, %zu)", reinterpret_cast<uintptr_t>(ptr), reinterpret_cast<uintptr_t>(mem_), reinterpret_cast<uintptr_t>(std::next(mem_, size()))); 5580e6b6b59SJacob Faibussowitsch } 5593ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 5600e6b6b59SJacob Faibussowitsch } 5610e6b6b59SJacob Faibussowitsch 562d71ae5a4SJacob Faibussowitsch namespace detail 563d71ae5a4SJacob Faibussowitsch { 5640e6b6b59SJacob Faibussowitsch 5650e6b6b59SJacob Faibussowitsch template <typename T> 5660e6b6b59SJacob Faibussowitsch struct real_type { 5670e6b6b59SJacob Faibussowitsch using type = T; 5680e6b6b59SJacob Faibussowitsch }; 5690e6b6b59SJacob Faibussowitsch 5700e6b6b59SJacob Faibussowitsch template <> 5710e6b6b59SJacob Faibussowitsch struct real_type<PetscScalar> { 5720e6b6b59SJacob Faibussowitsch using type = PetscReal; 5730e6b6b59SJacob Faibussowitsch }; 5740e6b6b59SJacob Faibussowitsch 5750e6b6b59SJacob Faibussowitsch } // namespace detail 5760e6b6b59SJacob Faibussowitsch 5770e6b6b59SJacob Faibussowitsch template <typename T> 5780e6b6b59SJacob Faibussowitsch struct SegmentedMemoryPoolAllocatorBase { 5790e6b6b59SJacob Faibussowitsch using value_type = T; 5800e6b6b59SJacob Faibussowitsch using size_type = std::size_t; 5810e6b6b59SJacob Faibussowitsch using real_value_type = typename detail::real_type<T>::type; 5820e6b6b59SJacob Faibussowitsch 5830e6b6b59SJacob Faibussowitsch template <typename U> 584089fb57cSJacob Faibussowitsch static PetscErrorCode allocate(value_type **, size_type, const device::StreamBase<U> *) noexcept; 5850e6b6b59SJacob Faibussowitsch template <typename U> 586089fb57cSJacob Faibussowitsch static PetscErrorCode deallocate(value_type *, const device::StreamBase<U> *) noexcept; 5870e6b6b59SJacob Faibussowitsch template <typename U> 588089fb57cSJacob Faibussowitsch static PetscErrorCode zero(value_type *, size_type, const device::StreamBase<U> *) noexcept; 5890e6b6b59SJacob Faibussowitsch template <typename U> 590089fb57cSJacob Faibussowitsch static PetscErrorCode uninitialized_copy(value_type *, const value_type *, size_type, const device::StreamBase<U> *) noexcept; 5910e6b6b59SJacob Faibussowitsch template <typename U> 592089fb57cSJacob Faibussowitsch static PetscErrorCode set_canary(value_type *, size_type, const device::StreamBase<U> *) noexcept; 5930e6b6b59SJacob Faibussowitsch }; 5940e6b6b59SJacob Faibussowitsch 5950e6b6b59SJacob Faibussowitsch template <typename T> 5960e6b6b59SJacob Faibussowitsch template <typename U> 597d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::allocate(value_type **ptr, size_type n, const device::StreamBase<U> *) noexcept 598d71ae5a4SJacob Faibussowitsch { 5990e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 6000e6b6b59SJacob Faibussowitsch PetscCall(PetscMalloc1(n, ptr)); 6013ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6020e6b6b59SJacob Faibussowitsch } 6030e6b6b59SJacob Faibussowitsch 6040e6b6b59SJacob Faibussowitsch template <typename T> 6050e6b6b59SJacob Faibussowitsch template <typename U> 606d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::deallocate(value_type *ptr, const device::StreamBase<U> *) noexcept 607d71ae5a4SJacob Faibussowitsch { 6080e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 6090e6b6b59SJacob Faibussowitsch PetscCall(PetscFree(ptr)); 6103ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6110e6b6b59SJacob Faibussowitsch } 6120e6b6b59SJacob Faibussowitsch 6130e6b6b59SJacob Faibussowitsch template <typename T> 6140e6b6b59SJacob Faibussowitsch template <typename U> 615d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::zero(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept 616d71ae5a4SJacob Faibussowitsch { 6170e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 6180e6b6b59SJacob Faibussowitsch PetscCall(PetscArrayzero(ptr, n)); 6193ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6200e6b6b59SJacob Faibussowitsch } 6210e6b6b59SJacob Faibussowitsch 6220e6b6b59SJacob Faibussowitsch template <typename T> 6230e6b6b59SJacob Faibussowitsch template <typename U> 624d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::uninitialized_copy(value_type *dest, const value_type *src, size_type n, const device::StreamBase<U> *) noexcept 625d71ae5a4SJacob Faibussowitsch { 6260e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 6270e6b6b59SJacob Faibussowitsch PetscCall(PetscArraycpy(dest, src, n)); 6283ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6290e6b6b59SJacob Faibussowitsch } 6300e6b6b59SJacob Faibussowitsch 6310e6b6b59SJacob Faibussowitsch template <typename T> 6320e6b6b59SJacob Faibussowitsch template <typename U> 633d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::set_canary(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept 634d71ae5a4SJacob Faibussowitsch { 6350e6b6b59SJacob Faibussowitsch using limit_type = std::numeric_limits<real_value_type>; 6360e6b6b59SJacob Faibussowitsch constexpr value_type canary = limit_type::has_signaling_NaN ? limit_type::signaling_NaN() : limit_type::max(); 6370e6b6b59SJacob Faibussowitsch 6380e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 6390e6b6b59SJacob Faibussowitsch for (size_type i = 0; i < n; ++i) ptr[i] = canary; 6403ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 6410e6b6b59SJacob Faibussowitsch } 6420e6b6b59SJacob Faibussowitsch 6430e6b6b59SJacob Faibussowitsch } // namespace impl 6440e6b6b59SJacob Faibussowitsch 6450e6b6b59SJacob Faibussowitsch // ========================================================================================== 6460e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool 6476797ed33SJacob Faibussowitsch // 6486797ed33SJacob Faibussowitsch // Stream-aware async memory allocator. Holds a list of memory "blocks" which each control an 6496797ed33SJacob Faibussowitsch // allocated buffer. This buffer is further split into memory "chunks" which control 6506797ed33SJacob Faibussowitsch // consecutive, non-overlapping regions of the block. Chunks may be in 1 of 2 states: 6516797ed33SJacob Faibussowitsch // 6526797ed33SJacob Faibussowitsch // 1. Open: 6536797ed33SJacob Faibussowitsch // The chunk is free to be claimed by the next suitable allocation request. If the 6546797ed33SJacob Faibussowitsch // allocation request is made on the same stream as the chunk was deallocated on, no 6556797ed33SJacob Faibussowitsch // serialization needs to occur. If not, the allocating stream must wait for the 6566797ed33SJacob Faibussowitsch // event. Claiming the chunk "closes" the chunk. 6576797ed33SJacob Faibussowitsch // 6586797ed33SJacob Faibussowitsch // 2. Closed: 6596797ed33SJacob Faibussowitsch // The chunk has been claimed by an allocation request. It cannot be opened again until it 6606797ed33SJacob Faibussowitsch // is deallocated; doing so "opens" the chunk. 6616797ed33SJacob Faibussowitsch // 6626797ed33SJacob Faibussowitsch // Note that there does not need to be a chunk for every region, chunks are created to satisfy 6636797ed33SJacob Faibussowitsch // an allocation request. 6646797ed33SJacob Faibussowitsch // 6656797ed33SJacob Faibussowitsch // Thus there is usually a region of "unallocated" memory at the end of the buffer, which may 6666797ed33SJacob Faibussowitsch // be claimed by a newly created chunk if existing chunks cannot satisfy the allocation 6676797ed33SJacob Faibussowitsch // request. This region exists _only_ at the end, as there are no gaps between chunks. 6686797ed33SJacob Faibussowitsch // 6696797ed33SJacob Faibussowitsch // 6706797ed33SJacob Faibussowitsch // |----------------------------------------------------------------------------------------- 6716797ed33SJacob Faibussowitsch // | SegmentedMemoryPool 6726797ed33SJacob Faibussowitsch // | 6736797ed33SJacob Faibussowitsch // | ||-------------|| 6746797ed33SJacob Faibussowitsch // | || || ------------------------------------------------------------------- 6756797ed33SJacob Faibussowitsch // | || || | AAAAAAAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDXXXXXXXX... 6766797ed33SJacob Faibussowitsch // | || || | | | | | | 6776797ed33SJacob Faibussowitsch // | || || | x-----x-------x-----xx---------x---------x------x-----x 6786797ed33SJacob Faibussowitsch // | || MemoryBlock || -> | ------|-------------|----------|----------------|-------- 6796797ed33SJacob Faibussowitsch // | || || | | MemoryChunk | MemoryChunk | MemoryChunk | MemoryChunk | 6806797ed33SJacob Faibussowitsch // | || || | --------------------------------------------------------- 6816797ed33SJacob Faibussowitsch // | || || ------------------------------------------------------------------- 6826797ed33SJacob Faibussowitsch // | ||-------------|| 6836797ed33SJacob Faibussowitsch // | || || 6846797ed33SJacob Faibussowitsch // | || ... || 6856797ed33SJacob Faibussowitsch // | || || 6860e6b6b59SJacob Faibussowitsch // ========================================================================================== 6870e6b6b59SJacob Faibussowitsch 6880e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType = device::DefaultStream, typename AllocType = impl::SegmentedMemoryPoolAllocatorBase<MemType>, std::size_t DefaultChunkSize = 256> 6890e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool; 6900e6b6b59SJacob Faibussowitsch 6910e6b6b59SJacob Faibussowitsch // The actual memory pool class. It is in essence just a wrapper for a list of MemoryBlocks. 6920e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 6930e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool : public RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>> { 6940e6b6b59SJacob Faibussowitsch public: 6950e6b6b59SJacob Faibussowitsch using value_type = MemType; 6960e6b6b59SJacob Faibussowitsch using stream_type = StreamType; 6970e6b6b59SJacob Faibussowitsch using allocator_type = AllocType; 6980e6b6b59SJacob Faibussowitsch using block_type = impl::MemoryBlock<value_type, allocator_type, stream_type>; 6990e6b6b59SJacob Faibussowitsch using pool_type = std::deque<block_type>; 7000e6b6b59SJacob Faibussowitsch using size_type = typename block_type::size_type; 7010e6b6b59SJacob Faibussowitsch 7020e6b6b59SJacob Faibussowitsch explicit SegmentedMemoryPool(AllocType = AllocType{}, std::size_t = DefaultChunkSize) noexcept(std::is_nothrow_default_constructible<pool_type>::value); 7030e6b6b59SJacob Faibussowitsch 704089fb57cSJacob Faibussowitsch PetscErrorCode allocate(PetscInt, value_type **, const stream_type *, size_type = std::alignment_of<MemType>::value) noexcept; 705089fb57cSJacob Faibussowitsch PetscErrorCode deallocate(value_type **, const stream_type *) noexcept; 706089fb57cSJacob Faibussowitsch PetscErrorCode reallocate(PetscInt, value_type **, const stream_type *) noexcept; 7070e6b6b59SJacob Faibussowitsch 7080e6b6b59SJacob Faibussowitsch private: 7090e6b6b59SJacob Faibussowitsch pool_type pool_; 7100e6b6b59SJacob Faibussowitsch allocator_type allocator_; 7110e6b6b59SJacob Faibussowitsch size_type chunk_size_; 7120e6b6b59SJacob Faibussowitsch 713089fb57cSJacob Faibussowitsch PetscErrorCode make_block_(size_type, const stream_type *) noexcept; 7140e6b6b59SJacob Faibussowitsch 7150e6b6b59SJacob Faibussowitsch friend class RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>>; 716089fb57cSJacob Faibussowitsch PetscErrorCode register_finalize_(const stream_type *) noexcept; 717089fb57cSJacob Faibussowitsch PetscErrorCode finalize_() noexcept; 7186797ed33SJacob Faibussowitsch 719089fb57cSJacob Faibussowitsch PetscErrorCode allocate_(size_type, value_type **, const stream_type *) noexcept; 7200e6b6b59SJacob Faibussowitsch }; 7210e6b6b59SJacob Faibussowitsch 7220e6b6b59SJacob Faibussowitsch // ========================================================================================== 7230e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Private API 7240e6b6b59SJacob Faibussowitsch // ========================================================================================== 7250e6b6b59SJacob Faibussowitsch 7260e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 727d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::make_block_(size_type size, const stream_type *stream) noexcept 728d71ae5a4SJacob Faibussowitsch { 7290e6b6b59SJacob Faibussowitsch const auto block_size = std::max(size, chunk_size_); 7300e6b6b59SJacob Faibussowitsch 7310e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 7320e6b6b59SJacob Faibussowitsch PetscCallCXX(pool_.emplace_back(&allocator_, block_size, stream)); 7330e6b6b59SJacob Faibussowitsch PetscCall(PetscInfo(nullptr, "Allocated new block of size %zu, total %zu blocks\n", block_size, pool_.size())); 7343ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 7350e6b6b59SJacob Faibussowitsch } 7360e6b6b59SJacob Faibussowitsch 7370e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 738d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::register_finalize_(const stream_type *stream) noexcept 739d71ae5a4SJacob Faibussowitsch { 7400e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 7410e6b6b59SJacob Faibussowitsch PetscCall(make_block_(chunk_size_, stream)); 7423ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 7430e6b6b59SJacob Faibussowitsch } 7440e6b6b59SJacob Faibussowitsch 7450e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 746d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::finalize_() noexcept 747d71ae5a4SJacob Faibussowitsch { 7480e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 7490e6b6b59SJacob Faibussowitsch PetscCallCXX(pool_.clear()); 7500e6b6b59SJacob Faibussowitsch chunk_size_ = DefaultChunkSize; 7513ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 7520e6b6b59SJacob Faibussowitsch } 7530e6b6b59SJacob Faibussowitsch 7546797ed33SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 755d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate_(size_type size, value_type **ptr, const stream_type *stream) noexcept 756d71ae5a4SJacob Faibussowitsch { 7576797ed33SJacob Faibussowitsch auto found = false; 7586797ed33SJacob Faibussowitsch 7596797ed33SJacob Faibussowitsch PetscFunctionBegin; 760503aa7efSJacob Faibussowitsch PetscCall(this->register_finalize(stream)); 7616797ed33SJacob Faibussowitsch for (auto &block : pool_) { 7626797ed33SJacob Faibussowitsch PetscCall(block.try_allocate_chunk(size, ptr, stream, &found)); 7633ba16761SJacob Faibussowitsch if (PetscLikely(found)) PetscFunctionReturn(PETSC_SUCCESS); 7646797ed33SJacob Faibussowitsch } 7656797ed33SJacob Faibussowitsch 7666797ed33SJacob Faibussowitsch PetscCall(PetscInfo(nullptr, "Could not find an open block in the pool (%zu blocks) (requested size %zu), allocating new block\n", pool_.size(), size)); 7676797ed33SJacob Faibussowitsch // if we are here we couldn't find an open block in the pool, so make a new block 7686797ed33SJacob Faibussowitsch PetscCall(make_block_(size, stream)); 7696797ed33SJacob Faibussowitsch // and assign it 7706797ed33SJacob Faibussowitsch PetscCall(pool_.back().try_allocate_chunk(size, ptr, stream, &found)); 7716797ed33SJacob Faibussowitsch PetscAssert(found, PETSC_COMM_SELF, PETSC_ERR_MEM, "Failed to get a suitable memory chunk (of size %zu) from newly allocated memory block (size %zu)", size, pool_.back().size()); 7723ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 7736797ed33SJacob Faibussowitsch } 7746797ed33SJacob Faibussowitsch 7750e6b6b59SJacob Faibussowitsch // ========================================================================================== 7760e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Public API 7770e6b6b59SJacob Faibussowitsch // ========================================================================================== 7780e6b6b59SJacob Faibussowitsch 7790e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 780d71ae5a4SJacob Faibussowitsch inline SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::SegmentedMemoryPool(AllocType alloc, std::size_t size) noexcept(std::is_nothrow_default_constructible<pool_type>::value) : allocator_(std::move(alloc)), chunk_size_(size) 781d71ae5a4SJacob Faibussowitsch { 782d71ae5a4SJacob Faibussowitsch } 7830e6b6b59SJacob Faibussowitsch 7840e6b6b59SJacob Faibussowitsch /* 7850e6b6b59SJacob Faibussowitsch SegmentedMemoryPool::allocate - get an allocation from the memory pool 7860e6b6b59SJacob Faibussowitsch 7870e6b6b59SJacob Faibussowitsch Input Parameters: 7880e6b6b59SJacob Faibussowitsch + req_size - size (in elements) to get 7890e6b6b59SJacob Faibussowitsch . ptr - the pointer to hold the allocation 7900e6b6b59SJacob Faibussowitsch - stream - the stream on which to get the allocation 7910e6b6b59SJacob Faibussowitsch 7920e6b6b59SJacob Faibussowitsch Output Parameter: 7930e6b6b59SJacob Faibussowitsch . ptr - the pointer holding the allocation 7940e6b6b59SJacob Faibussowitsch 7950e6b6b59SJacob Faibussowitsch Notes: 7960e6b6b59SJacob Faibussowitsch req_size cannot be negative. If req_size if zero, ptr is set to nullptr 7970e6b6b59SJacob Faibussowitsch */ 7980e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 799d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate(PetscInt req_size, value_type **ptr, const stream_type *stream, size_type alignment) noexcept 800d71ae5a4SJacob Faibussowitsch { 8016797ed33SJacob Faibussowitsch value_type *ret_ptr = nullptr; 8020e6b6b59SJacob Faibussowitsch 8030e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 8040e6b6b59SJacob Faibussowitsch PetscAssert(req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", req_size); 8054f572ea9SToby Isaac PetscAssertPointer(ptr, 2); 8064f572ea9SToby Isaac PetscAssertPointer(stream, 3); 8076797ed33SJacob Faibussowitsch if (req_size) { 8086797ed33SJacob Faibussowitsch const auto size = static_cast<size_type>(req_size); 8096797ed33SJacob Faibussowitsch auto aligned_size = alignment == alignof(char) ? size : size + alignment; 8106797ed33SJacob Faibussowitsch void *vptr = nullptr; 8110e6b6b59SJacob Faibussowitsch 8126797ed33SJacob Faibussowitsch PetscCall(allocate_(aligned_size, &ret_ptr, stream)); 8136797ed33SJacob Faibussowitsch vptr = ret_ptr; 8146797ed33SJacob Faibussowitsch std::align(alignment, size, vptr, aligned_size); 8156797ed33SJacob Faibussowitsch ret_ptr = reinterpret_cast<value_type *>(vptr); 8166797ed33SJacob Faibussowitsch // sets memory to NaN or infinity depending on the type to catch out uninitialized memory 8176797ed33SJacob Faibussowitsch // accesses. 8186797ed33SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) PetscCall(allocator_.set_canary(ret_ptr, size, stream)); 8196797ed33SJacob Faibussowitsch } 8206797ed33SJacob Faibussowitsch *ptr = ret_ptr; 8213ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 8220e6b6b59SJacob Faibussowitsch } 8230e6b6b59SJacob Faibussowitsch 8240e6b6b59SJacob Faibussowitsch /* 8250e6b6b59SJacob Faibussowitsch SegmentedMemoryPool::deallocate - release a pointer back to the memory pool 8260e6b6b59SJacob Faibussowitsch 8270e6b6b59SJacob Faibussowitsch Input Parameters: 8280e6b6b59SJacob Faibussowitsch + ptr - the pointer to release 8290e6b6b59SJacob Faibussowitsch - stream - the stream to release it on 8300e6b6b59SJacob Faibussowitsch 8310e6b6b59SJacob Faibussowitsch Notes: 8320e6b6b59SJacob Faibussowitsch If ptr is not owned by the pool it is unchanged. 8330e6b6b59SJacob Faibussowitsch */ 8340e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 835d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::deallocate(value_type **ptr, const stream_type *stream) noexcept 836d71ae5a4SJacob Faibussowitsch { 8370e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 8384f572ea9SToby Isaac PetscAssertPointer(ptr, 1); 8394f572ea9SToby Isaac PetscAssertPointer(stream, 2); 8400e6b6b59SJacob Faibussowitsch // nobody owns a nullptr, and if they do then they have bigger problems 8413ba16761SJacob Faibussowitsch if (!*ptr) PetscFunctionReturn(PETSC_SUCCESS); 8420e6b6b59SJacob Faibussowitsch for (auto &block : pool_) { 8430e6b6b59SJacob Faibussowitsch auto found = false; 8440e6b6b59SJacob Faibussowitsch 8450e6b6b59SJacob Faibussowitsch PetscCall(block.try_deallocate_chunk(ptr, stream, &found)); 8460e6b6b59SJacob Faibussowitsch if (PetscLikely(found)) break; 8470e6b6b59SJacob Faibussowitsch } 8483ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 8490e6b6b59SJacob Faibussowitsch } 8500e6b6b59SJacob Faibussowitsch 8510e6b6b59SJacob Faibussowitsch /* 8520e6b6b59SJacob Faibussowitsch SegmentedMemoryPool::reallocate - Resize an allocated buffer 8530e6b6b59SJacob Faibussowitsch 8540e6b6b59SJacob Faibussowitsch Input Parameters: 8550e6b6b59SJacob Faibussowitsch + new_req_size - the new buffer size 8560e6b6b59SJacob Faibussowitsch . ptr - pointer to the buffer 8570e6b6b59SJacob Faibussowitsch - stream - stream to resize with 8580e6b6b59SJacob Faibussowitsch 859d5b43468SJose E. Roman Output Parameter: 8600e6b6b59SJacob Faibussowitsch . ptr - pointer to the new region 8610e6b6b59SJacob Faibussowitsch 8620e6b6b59SJacob Faibussowitsch Notes: 8630e6b6b59SJacob Faibussowitsch ptr must have been allocated by the pool. 8640e6b6b59SJacob Faibussowitsch 8650e6b6b59SJacob Faibussowitsch It's OK to shrink the buffer, even down to 0 (in which case it is just deallocated). 8660e6b6b59SJacob Faibussowitsch */ 8670e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize> 868d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::reallocate(PetscInt new_req_size, value_type **ptr, const stream_type *stream) noexcept 869d71ae5a4SJacob Faibussowitsch { 8700e6b6b59SJacob Faibussowitsch using chunk_type = typename block_type::chunk_type; 8710e6b6b59SJacob Faibussowitsch 8720e6b6b59SJacob Faibussowitsch const auto new_size = static_cast<size_type>(new_req_size); 8730e6b6b59SJacob Faibussowitsch const auto old_ptr = *ptr; 8740e6b6b59SJacob Faibussowitsch chunk_type *chunk = nullptr; 8750e6b6b59SJacob Faibussowitsch 8760e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 8770e6b6b59SJacob Faibussowitsch PetscAssert(new_req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", new_req_size); 8784f572ea9SToby Isaac PetscAssertPointer(ptr, 2); 8794f572ea9SToby Isaac PetscAssertPointer(stream, 3); 8800e6b6b59SJacob Faibussowitsch 8810e6b6b59SJacob Faibussowitsch // if reallocating to zero, just free 8820e6b6b59SJacob Faibussowitsch if (PetscUnlikely(new_size == 0)) { 8830e6b6b59SJacob Faibussowitsch PetscCall(deallocate(ptr, stream)); 8843ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 8850e6b6b59SJacob Faibussowitsch } 8860e6b6b59SJacob Faibussowitsch 8870e6b6b59SJacob Faibussowitsch // search the blocks for the owning chunk 8880e6b6b59SJacob Faibussowitsch for (auto &block : pool_) { 8890e6b6b59SJacob Faibussowitsch PetscCall(block.try_find_chunk(old_ptr, &chunk)); 8900e6b6b59SJacob Faibussowitsch if (chunk) break; // found 8910e6b6b59SJacob Faibussowitsch } 8920e6b6b59SJacob Faibussowitsch PetscAssert(chunk, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Memory pool does not own %p, so cannot reallocate it", *ptr); 8930e6b6b59SJacob Faibussowitsch 8940e6b6b59SJacob Faibussowitsch if (chunk->capacity() < new_size) { 8950e6b6b59SJacob Faibussowitsch // chunk does not have enough room, need to grab a fresh chunk and copy to it 8960e6b6b59SJacob Faibussowitsch *ptr = nullptr; 8970e6b6b59SJacob Faibussowitsch PetscCall(chunk->release(stream)); 8980e6b6b59SJacob Faibussowitsch PetscCall(allocate(new_size, ptr, stream)); 8990e6b6b59SJacob Faibussowitsch PetscCall(allocator_.uninitialized_copy(*ptr, old_ptr, new_size, stream)); 9000e6b6b59SJacob Faibussowitsch } else { 9010e6b6b59SJacob Faibussowitsch // chunk had enough room we can simply grow (or shrink) to fit the new size 9020e6b6b59SJacob Faibussowitsch PetscCall(chunk->resize(new_size)); 9030e6b6b59SJacob Faibussowitsch } 9043ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 9050e6b6b59SJacob Faibussowitsch } 9060e6b6b59SJacob Faibussowitsch 9070e6b6b59SJacob Faibussowitsch } // namespace memory 9080e6b6b59SJacob Faibussowitsch 9090e6b6b59SJacob Faibussowitsch } // namespace Petsc 910