xref: /petsc/src/sys/objects/device/impls/segmentedmempool.hpp (revision 6797ed33bfc5684cca29e5d9b9bbfc6c8aac93e0)
10e6b6b59SJacob Faibussowitsch #ifndef PETSC_SEGMENTEDMEMPOOL_HPP
20e6b6b59SJacob Faibussowitsch #define PETSC_SEGMENTEDMEMPOOL_HPP
30e6b6b59SJacob Faibussowitsch 
40e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h>
5*6797ed33SJacob Faibussowitsch 
60e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/macros.hpp>
70e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp>
80e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/utility.hpp>
90e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp>
10*6797ed33SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp>
110e6b6b59SJacob Faibussowitsch 
120e6b6b59SJacob Faibussowitsch #include <limits>
130e6b6b59SJacob Faibussowitsch #include <deque>
140e6b6b59SJacob Faibussowitsch #include <vector>
150e6b6b59SJacob Faibussowitsch 
160e6b6b59SJacob Faibussowitsch namespace Petsc {
170e6b6b59SJacob Faibussowitsch 
180e6b6b59SJacob Faibussowitsch namespace device {
190e6b6b59SJacob Faibussowitsch 
200e6b6b59SJacob Faibussowitsch template <typename T>
210e6b6b59SJacob Faibussowitsch class StreamBase {
220e6b6b59SJacob Faibussowitsch public:
230e6b6b59SJacob Faibussowitsch   using id_type      = int;
240e6b6b59SJacob Faibussowitsch   using derived_type = T;
250e6b6b59SJacob Faibussowitsch 
260e6b6b59SJacob Faibussowitsch   static const id_type INVALID_ID;
270e6b6b59SJacob Faibussowitsch 
280e6b6b59SJacob Faibussowitsch   // needed so that dependent auto works, see veccupmimpl.h for a detailed discussion
290e6b6b59SJacob Faibussowitsch   template <typename U = T>
300e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD auto get_stream() const noexcept PETSC_DECLTYPE_AUTO_RETURNS(static_cast<const U &>(*this).get_stream_());
310e6b6b59SJacob Faibussowitsch 
320e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD id_type get_id() const noexcept { return static_cast<const T &>(*this).get_id_(); }
330e6b6b59SJacob Faibussowitsch 
340e6b6b59SJacob Faibussowitsch   template <typename E>
350e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode record_event(E &&event) const noexcept {
360e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).record_event_(std::forward<E>(event));
370e6b6b59SJacob Faibussowitsch   }
380e6b6b59SJacob Faibussowitsch 
390e6b6b59SJacob Faibussowitsch   template <typename E>
400e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode wait_for_event(E &&event) const noexcept {
410e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).wait_for_(std::forward<E>(event));
420e6b6b59SJacob Faibussowitsch   }
430e6b6b59SJacob Faibussowitsch 
440e6b6b59SJacob Faibussowitsch protected:
450e6b6b59SJacob Faibussowitsch   constexpr StreamBase() noexcept = default;
460e6b6b59SJacob Faibussowitsch 
470e6b6b59SJacob Faibussowitsch   struct default_event_type { };
480e6b6b59SJacob Faibussowitsch   using default_stream_type = std::nullptr_t;
490e6b6b59SJacob Faibussowitsch 
500e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr default_stream_type get_stream_() noexcept { return nullptr; }
510e6b6b59SJacob Faibussowitsch 
520e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr id_type get_id_() noexcept { return 0; }
530e6b6b59SJacob Faibussowitsch 
540e6b6b59SJacob Faibussowitsch   template <typename U = T>
550e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr PetscErrorCode record_event_(const typename U::event_type &) noexcept {
560e6b6b59SJacob Faibussowitsch     return 0;
570e6b6b59SJacob Faibussowitsch   }
580e6b6b59SJacob Faibussowitsch 
590e6b6b59SJacob Faibussowitsch   template <typename U = T>
600e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr PetscErrorCode wait_for_(const typename U::event_type &) noexcept {
610e6b6b59SJacob Faibussowitsch     return 0;
620e6b6b59SJacob Faibussowitsch   }
630e6b6b59SJacob Faibussowitsch };
640e6b6b59SJacob Faibussowitsch 
650e6b6b59SJacob Faibussowitsch template <typename T>
660e6b6b59SJacob Faibussowitsch const typename StreamBase<T>::id_type StreamBase<T>::INVALID_ID = -1;
670e6b6b59SJacob Faibussowitsch 
680e6b6b59SJacob Faibussowitsch struct DefaultStream : StreamBase<DefaultStream> {
690e6b6b59SJacob Faibussowitsch   using stream_type = typename StreamBase<DefaultStream>::default_stream_type;
700e6b6b59SJacob Faibussowitsch   using id_type     = typename StreamBase<DefaultStream>::id_type;
710e6b6b59SJacob Faibussowitsch   using event_type  = typename StreamBase<DefaultStream>::default_event_type;
720e6b6b59SJacob Faibussowitsch };
730e6b6b59SJacob Faibussowitsch 
740e6b6b59SJacob Faibussowitsch } // namespace device
750e6b6b59SJacob Faibussowitsch 
760e6b6b59SJacob Faibussowitsch namespace memory {
770e6b6b59SJacob Faibussowitsch 
780e6b6b59SJacob Faibussowitsch namespace impl {
790e6b6b59SJacob Faibussowitsch 
800e6b6b59SJacob Faibussowitsch // ==========================================================================================
810e6b6b59SJacob Faibussowitsch // MemoryChunk
820e6b6b59SJacob Faibussowitsch //
830e6b6b59SJacob Faibussowitsch // Represents a checked-out region of a MemoryBlock. Tracks the offset into the owning
840e6b6b59SJacob Faibussowitsch // MemoryBlock and its size/capacity
850e6b6b59SJacob Faibussowitsch // ==========================================================================================
860e6b6b59SJacob Faibussowitsch 
870e6b6b59SJacob Faibussowitsch template <typename EventType>
880e6b6b59SJacob Faibussowitsch class MemoryChunk {
890e6b6b59SJacob Faibussowitsch public:
900e6b6b59SJacob Faibussowitsch   using event_type = EventType;
910e6b6b59SJacob Faibussowitsch   using size_type  = std::size_t;
920e6b6b59SJacob Faibussowitsch 
930e6b6b59SJacob Faibussowitsch   MemoryChunk(size_type, size_type) noexcept;
940e6b6b59SJacob Faibussowitsch   explicit MemoryChunk(size_type) noexcept;
950e6b6b59SJacob Faibussowitsch 
960e6b6b59SJacob Faibussowitsch   MemoryChunk(MemoryChunk &&) noexcept;
970e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(MemoryChunk &&) noexcept;
980e6b6b59SJacob Faibussowitsch 
990e6b6b59SJacob Faibussowitsch   MemoryChunk(const MemoryChunk &) noexcept            = delete;
1000e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(const MemoryChunk &) noexcept = delete;
1010e6b6b59SJacob Faibussowitsch 
1020e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type start() const noexcept { return start_; }
1030e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
1040e6b6b59SJacob Faibussowitsch   // REVIEW ME:
1050e6b6b59SJacob Faibussowitsch   // make this an actual field, normally each chunk shrinks_to_fit() on begin claimed, but in
1060e6b6b59SJacob Faibussowitsch   // theory only the last chunk needs to do this
1070e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type capacity() const noexcept { return size_; }
1080e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type total_offset() const noexcept { return start() + size(); }
1090e6b6b59SJacob Faibussowitsch 
1100e6b6b59SJacob Faibussowitsch   template <typename U>
1110e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode release(const device::StreamBase<U> *) noexcept;
1120e6b6b59SJacob Faibussowitsch   template <typename U>
1130e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode claim(const device::StreamBase<U> *, size_type, bool *, bool = false) noexcept;
1140e6b6b59SJacob Faibussowitsch   template <typename U>
1150e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool           can_claim(const device::StreamBase<U> *, size_type, bool) const noexcept;
1160e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode resize(size_type) noexcept;
117*6797ed33SJacob Faibussowitsch   PETSC_NODISCARD bool           contains(size_type) const noexcept;
1180e6b6b59SJacob Faibussowitsch 
1190e6b6b59SJacob Faibussowitsch private:
1200e6b6b59SJacob Faibussowitsch   // clang-format off
1210e6b6b59SJacob Faibussowitsch   event_type      event_{};          // event recorded when the chunk was released
1220e6b6b59SJacob Faibussowitsch   bool            open_      = true; // is this chunk open?
1230e6b6b59SJacob Faibussowitsch   // id of the last stream to use the chunk, populated on release
1240e6b6b59SJacob Faibussowitsch   int             stream_id_ = device::DefaultStream::INVALID_ID;
1250e6b6b59SJacob Faibussowitsch   size_type       size_      = 0;    // size of the chunk
1260e6b6b59SJacob Faibussowitsch   const size_type start_     = 0;    // offset from the start of the owning block
1270e6b6b59SJacob Faibussowitsch   // clang-format on
128*6797ed33SJacob Faibussowitsch 
1290e6b6b59SJacob Faibussowitsch   template <typename U>
1300e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool stream_compat_(const device::StreamBase<U> *) const noexcept;
1310e6b6b59SJacob Faibussowitsch };
1320e6b6b59SJacob Faibussowitsch 
1330e6b6b59SJacob Faibussowitsch // ==========================================================================================
1340e6b6b59SJacob Faibussowitsch // MemoryChunk - Private API
1350e6b6b59SJacob Faibussowitsch // ==========================================================================================
1360e6b6b59SJacob Faibussowitsch 
1370e6b6b59SJacob Faibussowitsch // asks and answers the question: can this stream claim this chunk without serializing?
1380e6b6b59SJacob Faibussowitsch template <typename E>
1390e6b6b59SJacob Faibussowitsch template <typename U>
1400e6b6b59SJacob Faibussowitsch inline bool MemoryChunk<E>::stream_compat_(const device::StreamBase<U> *strm) const noexcept {
1410e6b6b59SJacob Faibussowitsch   return (stream_id_ == strm->INVALID_ID) || (stream_id_ == strm->get_id());
1420e6b6b59SJacob Faibussowitsch }
1430e6b6b59SJacob Faibussowitsch 
1440e6b6b59SJacob Faibussowitsch // ==========================================================================================
1450e6b6b59SJacob Faibussowitsch // MemoryChunk - Public API
1460e6b6b59SJacob Faibussowitsch // ==========================================================================================
1470e6b6b59SJacob Faibussowitsch 
1480e6b6b59SJacob Faibussowitsch template <typename E>
1490e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type start, size_type size) noexcept : size_(size), start_(start) { }
1500e6b6b59SJacob Faibussowitsch 
1510e6b6b59SJacob Faibussowitsch template <typename E>
1520e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type size) noexcept : MemoryChunk(0, size) { }
1530e6b6b59SJacob Faibussowitsch 
1540e6b6b59SJacob Faibussowitsch template <typename E>
1550e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(MemoryChunk<E> &&other) noexcept :
1560e6b6b59SJacob 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_)) { }
1570e6b6b59SJacob Faibussowitsch 
1580e6b6b59SJacob Faibussowitsch template <typename E>
1590e6b6b59SJacob Faibussowitsch inline MemoryChunk<E> &MemoryChunk<E>::operator=(MemoryChunk<E> &&other) noexcept {
1600e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1610e6b6b59SJacob Faibussowitsch   if (this != &other) {
1620e6b6b59SJacob Faibussowitsch     event_     = std::move(other.event_);
1630e6b6b59SJacob Faibussowitsch     open_      = util::exchange(other.open_, false);
1640e6b6b59SJacob Faibussowitsch     stream_id_ = util::exchange(other.stream_id_, device::DefaultStream::INVALID_ID);
1650e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
1660e6b6b59SJacob Faibussowitsch     start_     = std::move(other.start_);
1670e6b6b59SJacob Faibussowitsch   }
1680e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
1690e6b6b59SJacob Faibussowitsch }
1700e6b6b59SJacob Faibussowitsch 
1710e6b6b59SJacob Faibussowitsch /*
1720e6b6b59SJacob Faibussowitsch   MemoryChunk::release - release a chunk on a stream
1730e6b6b59SJacob Faibussowitsch 
1740e6b6b59SJacob Faibussowitsch   Input Parameter:
1750e6b6b59SJacob Faibussowitsch . stream - the stream to release the chunk with
1760e6b6b59SJacob Faibussowitsch 
1770e6b6b59SJacob Faibussowitsch   Notes:
1780e6b6b59SJacob Faibussowitsch   Inserts a release operation on stream and records the state of stream at the time this
1790e6b6b59SJacob Faibussowitsch   routine was called.
1800e6b6b59SJacob Faibussowitsch 
1810e6b6b59SJacob Faibussowitsch   Future allocation requests which attempt to claim the chunk on the same stream may re-acquire
1820e6b6b59SJacob Faibussowitsch   the chunk without serialization.
1830e6b6b59SJacob Faibussowitsch 
1840e6b6b59SJacob Faibussowitsch   If another stream attempts to claim the chunk they must wait for the recorded event before
1850e6b6b59SJacob Faibussowitsch   claiming the chunk.
1860e6b6b59SJacob Faibussowitsch */
1870e6b6b59SJacob Faibussowitsch template <typename E>
1880e6b6b59SJacob Faibussowitsch template <typename U>
1890e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::release(const device::StreamBase<U> *stream) noexcept {
1900e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1910e6b6b59SJacob Faibussowitsch   open_      = true;
1920e6b6b59SJacob Faibussowitsch   stream_id_ = stream->get_id();
1930e6b6b59SJacob Faibussowitsch   PetscCall(stream->record_event(event_));
1940e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1950e6b6b59SJacob Faibussowitsch }
1960e6b6b59SJacob Faibussowitsch 
1970e6b6b59SJacob Faibussowitsch /*
1980e6b6b59SJacob Faibussowitsch   MemoryChunk::claim - attempt to claim a particular chunk
1990e6b6b59SJacob Faibussowitsch 
2000e6b6b59SJacob Faibussowitsch   Input Parameters:
2010e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2020e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2030e6b6b59SJacob Faibussowitsch - serialize - (optional, false) whether the claimant allows serialization
2040e6b6b59SJacob Faibussowitsch 
2050e6b6b59SJacob Faibussowitsch   Output Parameter:
2060e6b6b59SJacob Faibussowitsch . success - true if the chunk was claimed, false otherwise
2070e6b6b59SJacob Faibussowitsch */
2080e6b6b59SJacob Faibussowitsch template <typename E>
2090e6b6b59SJacob Faibussowitsch template <typename U>
2100e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::claim(const device::StreamBase<U> *stream, size_type req_size, bool *success, bool serialize) noexcept {
2110e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2120e6b6b59SJacob Faibussowitsch   if ((*success = can_claim(stream, req_size, serialize))) {
2130e6b6b59SJacob Faibussowitsch     if (serialize && !stream_compat_(stream)) PetscCall(stream->wait_for_event(event_));
2140e6b6b59SJacob Faibussowitsch     PetscCall(resize(req_size));
2150e6b6b59SJacob Faibussowitsch     open_ = false;
2160e6b6b59SJacob Faibussowitsch   }
2170e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2180e6b6b59SJacob Faibussowitsch }
2190e6b6b59SJacob Faibussowitsch 
2200e6b6b59SJacob Faibussowitsch /*
2210e6b6b59SJacob Faibussowitsch   MemoryChunk::can_claim - test whether a particular chunk can be claimed
2220e6b6b59SJacob Faibussowitsch 
2230e6b6b59SJacob Faibussowitsch   Input Parameters:
2240e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2250e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2260e6b6b59SJacob Faibussowitsch - serialize - whether the claimant allows serialization
2270e6b6b59SJacob Faibussowitsch 
2280e6b6b59SJacob Faibussowitsch   Output:
2290e6b6b59SJacob Faibussowitsch . [return] - true if the chunk is claimable given the configuration, false otherwise
2300e6b6b59SJacob Faibussowitsch */
2310e6b6b59SJacob Faibussowitsch template <typename E>
2320e6b6b59SJacob Faibussowitsch template <typename U>
2330e6b6b59SJacob Faibussowitsch inline bool MemoryChunk<E>::can_claim(const device::StreamBase<U> *stream, size_type req_size, bool serialize) const noexcept {
2340e6b6b59SJacob Faibussowitsch   if (open_ && (req_size <= capacity())) {
2350e6b6b59SJacob Faibussowitsch     // fully compatible
2360e6b6b59SJacob Faibussowitsch     if (stream_compat_(stream)) return true;
2370e6b6b59SJacob Faibussowitsch     // stream wasn't compatible, but could claim if we serialized
2380e6b6b59SJacob Faibussowitsch     if (serialize) return true;
2390e6b6b59SJacob Faibussowitsch     // incompatible stream and did not want to serialize
2400e6b6b59SJacob Faibussowitsch   }
2410e6b6b59SJacob Faibussowitsch   return false;
2420e6b6b59SJacob Faibussowitsch }
2430e6b6b59SJacob Faibussowitsch 
2440e6b6b59SJacob Faibussowitsch /*
2450e6b6b59SJacob Faibussowitsch   MemoryChunk::resize - grow a chunk to new size
2460e6b6b59SJacob Faibussowitsch 
2470e6b6b59SJacob Faibussowitsch   Input Parameter:
2480e6b6b59SJacob Faibussowitsch . newsize - the new size Requested
2490e6b6b59SJacob Faibussowitsch 
2500e6b6b59SJacob Faibussowitsch   Notes:
2510e6b6b59SJacob Faibussowitsch   newsize cannot be larger than capacity
2520e6b6b59SJacob Faibussowitsch */
2530e6b6b59SJacob Faibussowitsch template <typename E>
2540e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::resize(size_type newsize) noexcept {
2550e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2560e6b6b59SJacob Faibussowitsch   PetscAssert(newsize <= capacity(), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "New size %zu larger than capacity %zu", newsize, capacity());
2570e6b6b59SJacob Faibussowitsch   size_ = newsize;
2580e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2590e6b6b59SJacob Faibussowitsch }
2600e6b6b59SJacob Faibussowitsch 
261*6797ed33SJacob Faibussowitsch /*
262*6797ed33SJacob Faibussowitsch   MemoryChunk::contains - query whether a memory chunk contains a particular offset
263*6797ed33SJacob Faibussowitsch 
264*6797ed33SJacob Faibussowitsch   Input Parameters:
265*6797ed33SJacob Faibussowitsch . offset - The offset from the MemoryBlock start
266*6797ed33SJacob Faibussowitsch 
267*6797ed33SJacob Faibussowitsch   Notes:
268*6797ed33SJacob Faibussowitsch   Returns true if the chunk contains the offset, false otherwise
269*6797ed33SJacob Faibussowitsch */
270*6797ed33SJacob Faibussowitsch template <typename E>
271*6797ed33SJacob Faibussowitsch inline bool MemoryChunk<E>::contains(size_type offset) const noexcept {
272*6797ed33SJacob Faibussowitsch   return (offset >= start()) && (offset < total_offset());
273*6797ed33SJacob Faibussowitsch }
274*6797ed33SJacob Faibussowitsch 
2750e6b6b59SJacob Faibussowitsch // ==========================================================================================
2760e6b6b59SJacob Faibussowitsch // MemoryBlock
2770e6b6b59SJacob Faibussowitsch //
2780e6b6b59SJacob Faibussowitsch // A "memory block" manager, which owns the pointer to a particular memory range. Retrieving
2790e6b6b59SJacob Faibussowitsch // and restoring a block is thread-safe (so may be used by multiple device streams).
2800e6b6b59SJacob Faibussowitsch // ==========================================================================================
2810e6b6b59SJacob Faibussowitsch 
2820e6b6b59SJacob Faibussowitsch template <typename T, typename AllocatorType, typename StreamType>
2830e6b6b59SJacob Faibussowitsch class MemoryBlock {
2840e6b6b59SJacob Faibussowitsch public:
2850e6b6b59SJacob Faibussowitsch   using value_type      = T;
2860e6b6b59SJacob Faibussowitsch   using allocator_type  = AllocatorType;
2870e6b6b59SJacob Faibussowitsch   using stream_type     = StreamType;
2880e6b6b59SJacob Faibussowitsch   using event_type      = typename stream_type::event_type;
2890e6b6b59SJacob Faibussowitsch   using chunk_type      = MemoryChunk<event_type>;
2900e6b6b59SJacob Faibussowitsch   using size_type       = typename chunk_type::size_type;
2910e6b6b59SJacob Faibussowitsch   using chunk_list_type = std::vector<chunk_type>;
2920e6b6b59SJacob Faibussowitsch 
2930e6b6b59SJacob Faibussowitsch   template <typename U>
2940e6b6b59SJacob Faibussowitsch   MemoryBlock(allocator_type *, size_type, const device::StreamBase<U> *) noexcept;
2950e6b6b59SJacob Faibussowitsch 
2960e6b6b59SJacob Faibussowitsch   ~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value);
2970e6b6b59SJacob Faibussowitsch 
2980e6b6b59SJacob Faibussowitsch   MemoryBlock(MemoryBlock &&) noexcept;
2990e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(MemoryBlock &&) noexcept;
3000e6b6b59SJacob Faibussowitsch 
3010e6b6b59SJacob Faibussowitsch   // memory blocks are not copyable
3020e6b6b59SJacob Faibussowitsch   MemoryBlock(const MemoryBlock &)            = delete;
3030e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(const MemoryBlock &) = delete;
3040e6b6b59SJacob Faibussowitsch 
3050e6b6b59SJacob Faibussowitsch   /* --- actual functions --- */
3060e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_allocate_chunk(size_type, T **, const stream_type *, bool *) noexcept;
3070e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_deallocate_chunk(T **, const stream_type *, bool *) noexcept;
3080e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_find_chunk(const T *, chunk_type **) noexcept;
3090e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool           owns_pointer(const T *) const noexcept;
3100e6b6b59SJacob Faibussowitsch 
3110e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
3120e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type bytes() const noexcept { return sizeof(value_type) * size(); }
3130e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type num_chunks() const noexcept { return chunks_.size(); }
3140e6b6b59SJacob Faibussowitsch 
3150e6b6b59SJacob Faibussowitsch private:
3160e6b6b59SJacob Faibussowitsch   value_type     *mem_{};
3170e6b6b59SJacob Faibussowitsch   allocator_type *allocator_{};
3180e6b6b59SJacob Faibussowitsch   size_type       size_{};
3190e6b6b59SJacob Faibussowitsch   chunk_list_type chunks_{};
3200e6b6b59SJacob Faibussowitsch 
3210e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode clear_(const stream_type *) noexcept;
3220e6b6b59SJacob Faibussowitsch };
3230e6b6b59SJacob Faibussowitsch 
3240e6b6b59SJacob Faibussowitsch // ==========================================================================================
3250e6b6b59SJacob Faibussowitsch // MemoryBlock - Private API
3260e6b6b59SJacob Faibussowitsch // ==========================================================================================
3270e6b6b59SJacob Faibussowitsch 
3280e6b6b59SJacob Faibussowitsch // clear the memory block, called from destructors and move assignment/construction
3290e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3300e6b6b59SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode MemoryBlock<T, A, S>::clear_(const stream_type *stream) noexcept {
3310e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3320e6b6b59SJacob Faibussowitsch   if (PetscLikely(mem_)) {
3330e6b6b59SJacob Faibussowitsch     PetscCall(allocator_->deallocate(mem_, stream));
3340e6b6b59SJacob Faibussowitsch     mem_ = nullptr;
3350e6b6b59SJacob Faibussowitsch   }
3360e6b6b59SJacob Faibussowitsch   size_ = 0;
3370e6b6b59SJacob Faibussowitsch   PetscCallCXX(chunks_.clear());
3380e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
3390e6b6b59SJacob Faibussowitsch }
3400e6b6b59SJacob Faibussowitsch 
3410e6b6b59SJacob Faibussowitsch // ==========================================================================================
3420e6b6b59SJacob Faibussowitsch // MemoryBlock - Public API
3430e6b6b59SJacob Faibussowitsch // ==========================================================================================
3440e6b6b59SJacob Faibussowitsch 
3450e6b6b59SJacob Faibussowitsch // default constructor, allocates memory immediately
3460e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3470e6b6b59SJacob Faibussowitsch template <typename U>
348127bc8d3SJacob Faibussowitsch MemoryBlock<T, A, S>::MemoryBlock(allocator_type *alloc, size_type s, const device::StreamBase<U> *stream) noexcept : allocator_(alloc), size_(s) {
3490e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3500e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, alloc->allocate(&mem_, s, stream));
3510e6b6b59SJacob Faibussowitsch   PetscAssertAbort(mem_, PETSC_COMM_SELF, PETSC_ERR_MEM, "Failed to allocate memory block of size %zu", s);
3520e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3530e6b6b59SJacob Faibussowitsch }
3540e6b6b59SJacob Faibussowitsch 
3550e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3560e6b6b59SJacob Faibussowitsch MemoryBlock<T, A, S>::~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value) {
3570e6b6b59SJacob Faibussowitsch   stream_type stream;
3580e6b6b59SJacob Faibussowitsch 
3590e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3600e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
3610e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3620e6b6b59SJacob Faibussowitsch }
3630e6b6b59SJacob Faibussowitsch 
3640e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3650e6b6b59SJacob 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_)) { }
3660e6b6b59SJacob Faibussowitsch 
3670e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3680e6b6b59SJacob Faibussowitsch MemoryBlock<T, A, S> &MemoryBlock<T, A, S>::operator=(MemoryBlock &&other) noexcept {
3690e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3700e6b6b59SJacob Faibussowitsch   if (this != &other) {
3710e6b6b59SJacob Faibussowitsch     stream_type stream;
3720e6b6b59SJacob Faibussowitsch 
3730e6b6b59SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
3740e6b6b59SJacob Faibussowitsch     mem_       = util::exchange(other.mem_, nullptr);
3750e6b6b59SJacob Faibussowitsch     allocator_ = other.allocator_;
3760e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
3770e6b6b59SJacob Faibussowitsch     chunks_    = std::move(other.chunks_);
3780e6b6b59SJacob Faibussowitsch   }
3790e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
3800e6b6b59SJacob Faibussowitsch }
3810e6b6b59SJacob Faibussowitsch 
3820e6b6b59SJacob Faibussowitsch /*
3830e6b6b59SJacob Faibussowitsch   MemoryBock::owns_pointer - returns true if this block owns a pointer, false otherwise
3840e6b6b59SJacob Faibussowitsch */
3850e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3860e6b6b59SJacob Faibussowitsch inline bool MemoryBlock<T, A, S>::owns_pointer(const T *ptr) const noexcept {
3870e6b6b59SJacob Faibussowitsch   // each pool is linear in memory, so it suffices to check the bounds
3880e6b6b59SJacob Faibussowitsch   return (ptr >= mem_) && (ptr < std::next(mem_, size()));
3890e6b6b59SJacob Faibussowitsch }
3900e6b6b59SJacob Faibussowitsch 
3910e6b6b59SJacob Faibussowitsch /*
3920e6b6b59SJacob Faibussowitsch   MemoryBlock::try_allocate_chunk - try to get a chunk from this MemoryBlock
3930e6b6b59SJacob Faibussowitsch 
3940e6b6b59SJacob Faibussowitsch   Input Parameters:
3950e6b6b59SJacob Faibussowitsch + req_size - the requested size of the allocation (in elements)
3960e6b6b59SJacob Faibussowitsch . ptr      - ptr to fill
3970e6b6b59SJacob Faibussowitsch - stream   - stream to fill the pointer on
3980e6b6b59SJacob Faibussowitsch 
3990e6b6b59SJacob Faibussowitsch   Output Parameter:
4000e6b6b59SJacob Faibussowitsch . success  - true if chunk was gotten, false otherwise
4010e6b6b59SJacob Faibussowitsch 
4020e6b6b59SJacob Faibussowitsch   Notes:
4030e6b6b59SJacob Faibussowitsch   If the current memory could not satisfy the memory request, ptr is unchanged
4040e6b6b59SJacob Faibussowitsch */
4050e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
4060e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_allocate_chunk(size_type req_size, T **ptr, const stream_type *stream, bool *success) noexcept {
4070e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4080e6b6b59SJacob Faibussowitsch   *success = false;
4090e6b6b59SJacob Faibussowitsch   if (req_size <= size()) {
4100e6b6b59SJacob Faibussowitsch     const auto try_create_chunk = [&]() {
4110e6b6b59SJacob Faibussowitsch       const auto was_empty     = chunks_.empty();
4120e6b6b59SJacob Faibussowitsch       const auto block_alloced = was_empty ? 0 : chunks_.back().total_offset();
4130e6b6b59SJacob Faibussowitsch 
4140e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4150e6b6b59SJacob Faibussowitsch       if (block_alloced + req_size <= size()) {
4160e6b6b59SJacob Faibussowitsch         PetscCallCXX(chunks_.emplace_back(block_alloced, req_size));
4170e6b6b59SJacob Faibussowitsch         PetscCall(chunks_.back().claim(stream, req_size, success));
4180e6b6b59SJacob Faibussowitsch         *ptr = mem_ + block_alloced;
4190e6b6b59SJacob 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());
4200e6b6b59SJacob Faibussowitsch       }
4210e6b6b59SJacob Faibussowitsch       PetscFunctionReturn(0);
4220e6b6b59SJacob Faibussowitsch     };
4230e6b6b59SJacob Faibussowitsch     const auto try_find_open_chunk = [&](bool serialize = false) {
4240e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4250e6b6b59SJacob Faibussowitsch       for (auto &chunk : chunks_) {
4260e6b6b59SJacob Faibussowitsch         PetscCall(chunk.claim(stream, req_size, success, serialize));
4270e6b6b59SJacob Faibussowitsch         if (*success) {
4280e6b6b59SJacob Faibussowitsch           *ptr = mem_ + chunk.start();
4290e6b6b59SJacob Faibussowitsch           break;
4300e6b6b59SJacob Faibussowitsch         }
4310e6b6b59SJacob Faibussowitsch       }
4320e6b6b59SJacob Faibussowitsch       PetscFunctionReturn(0);
4330e6b6b59SJacob Faibussowitsch     };
434*6797ed33SJacob Faibussowitsch     const auto try_steal_other_stream_chunk = [&]() {
435*6797ed33SJacob Faibussowitsch       PetscFunctionBegin;
436*6797ed33SJacob Faibussowitsch       PetscCall(try_find_open_chunk(true));
437*6797ed33SJacob Faibussowitsch       PetscFunctionReturn(0);
438*6797ed33SJacob Faibussowitsch     };
4390e6b6b59SJacob Faibussowitsch 
4400e6b6b59SJacob Faibussowitsch     // search previously distributed chunks, but only claim one if it is on the same stream
4410e6b6b59SJacob Faibussowitsch     // as us
4420e6b6b59SJacob Faibussowitsch     PetscCall(try_find_open_chunk());
4430e6b6b59SJacob Faibussowitsch 
4440e6b6b59SJacob Faibussowitsch     // if we are here we couldn't reuse one of our own chunks so check first if the pool
4450e6b6b59SJacob Faibussowitsch     // has room for a new one
4460e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4470e6b6b59SJacob Faibussowitsch 
4480e6b6b59SJacob Faibussowitsch     // try pruning dead chunks off the back, note we do this regardless of whether we are
4490e6b6b59SJacob Faibussowitsch     // successful
4500e6b6b59SJacob Faibussowitsch     while (chunks_.back().can_claim(stream, 0, false)) {
4510e6b6b59SJacob Faibussowitsch       PetscCallCXX(chunks_.pop_back());
4520e6b6b59SJacob Faibussowitsch       if (chunks_.empty()) {
4530e6b6b59SJacob Faibussowitsch         // if chunks are empty it implies we have managed to claim (and subsequently destroy)
4540e6b6b59SJacob Faibussowitsch         // our own chunk twice! something has gone wrong
4550e6b6b59SJacob 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());
4560e6b6b59SJacob Faibussowitsch         break;
4570e6b6b59SJacob Faibussowitsch       }
4580e6b6b59SJacob Faibussowitsch     }
4590e6b6b59SJacob Faibussowitsch 
4600e6b6b59SJacob Faibussowitsch     // if previously unsuccessful see if enough space has opened up due to pruning. note that
4610e6b6b59SJacob Faibussowitsch     // if the chunk list was emptied from the pruning this call must succeed in allocating a
4620e6b6b59SJacob Faibussowitsch     // chunk, otherwise something is wrong
4630e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4640e6b6b59SJacob Faibussowitsch 
4650e6b6b59SJacob Faibussowitsch     // last resort, iterate over all chunks and see if we can steal one by waiting on the
4660e6b6b59SJacob Faibussowitsch     // current owner to finish using it
467*6797ed33SJacob Faibussowitsch     if (!*success) PetscCall(try_steal_other_stream_chunk());
4680e6b6b59SJacob Faibussowitsch   }
4690e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4700e6b6b59SJacob Faibussowitsch }
4710e6b6b59SJacob Faibussowitsch 
4720e6b6b59SJacob Faibussowitsch /*
4730e6b6b59SJacob Faibussowitsch   MemoryBlock::try_deallocate_chunk - try to restore a chunk to this MemoryBlock
4740e6b6b59SJacob Faibussowitsch 
4750e6b6b59SJacob Faibussowitsch   Input Parameters:
4760e6b6b59SJacob Faibussowitsch + ptr     - ptr to restore
4770e6b6b59SJacob Faibussowitsch - stream  - stream to restore the pointer on
4780e6b6b59SJacob Faibussowitsch 
4790e6b6b59SJacob Faibussowitsch   Output Parameter:
4800e6b6b59SJacob Faibussowitsch . success - true if chunk was restored, false otherwise
4810e6b6b59SJacob Faibussowitsch 
4820e6b6b59SJacob Faibussowitsch   Notes:
4830e6b6b59SJacob Faibussowitsch   ptr is set to nullptr on successful restore, and is unchanged otherwise. If the ptr is owned
4840e6b6b59SJacob Faibussowitsch   by this MemoryBlock then it is restored on stream. The same stream may recieve ptr again
4850e6b6b59SJacob Faibussowitsch   without synchronization, but other streams may not do so until either serializing or the
4860e6b6b59SJacob Faibussowitsch   stream is idle again.
4870e6b6b59SJacob Faibussowitsch */
4880e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
4890e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_deallocate_chunk(T **ptr, const stream_type *stream, bool *success) noexcept {
4900e6b6b59SJacob Faibussowitsch   chunk_type *chunk = nullptr;
4910e6b6b59SJacob Faibussowitsch 
4920e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4930e6b6b59SJacob Faibussowitsch   PetscCall(try_find_chunk(*ptr, &chunk));
4940e6b6b59SJacob Faibussowitsch   if (chunk) {
4950e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
4960e6b6b59SJacob Faibussowitsch     *ptr     = nullptr;
4970e6b6b59SJacob Faibussowitsch     *success = true;
4980e6b6b59SJacob Faibussowitsch   } else {
4990e6b6b59SJacob Faibussowitsch     *success = false;
5000e6b6b59SJacob Faibussowitsch   }
5010e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5020e6b6b59SJacob Faibussowitsch }
5030e6b6b59SJacob Faibussowitsch 
5040e6b6b59SJacob Faibussowitsch /*
5050e6b6b59SJacob Faibussowitsch   MemoryBlock::try_find_chunk - try to find the chunk which owns ptr
5060e6b6b59SJacob Faibussowitsch 
5070e6b6b59SJacob Faibussowitsch   Input Parameter:
5080e6b6b59SJacob Faibussowitsch . ptr - the pointer to lookk for
5090e6b6b59SJacob Faibussowitsch 
5100e6b6b59SJacob Faibussowitsch   Output Parameter:
5110e6b6b59SJacob Faibussowitsch . ret_chunk - pointer to the owning chunk or nullptr if not found
5120e6b6b59SJacob Faibussowitsch */
5130e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
5140e6b6b59SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_find_chunk(const T *ptr, chunk_type **ret_chunk) noexcept {
5150e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5160e6b6b59SJacob Faibussowitsch   *ret_chunk = nullptr;
5170e6b6b59SJacob Faibussowitsch   if (owns_pointer(ptr)) {
5180e6b6b59SJacob Faibussowitsch     const auto offset = static_cast<size_type>(ptr - mem_);
5190e6b6b59SJacob Faibussowitsch 
5200e6b6b59SJacob Faibussowitsch     for (auto &chunk : chunks_) {
521*6797ed33SJacob Faibussowitsch       if (chunk.contains(offset)) {
5220e6b6b59SJacob Faibussowitsch         *ret_chunk = &chunk;
5230e6b6b59SJacob Faibussowitsch         break;
5240e6b6b59SJacob Faibussowitsch       }
5250e6b6b59SJacob Faibussowitsch     }
5260e6b6b59SJacob Faibussowitsch 
5270e6b6b59SJacob 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())));
5280e6b6b59SJacob Faibussowitsch   }
5290e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5300e6b6b59SJacob Faibussowitsch }
5310e6b6b59SJacob Faibussowitsch 
5320e6b6b59SJacob Faibussowitsch namespace detail {
5330e6b6b59SJacob Faibussowitsch 
5340e6b6b59SJacob Faibussowitsch template <typename T>
5350e6b6b59SJacob Faibussowitsch struct real_type {
5360e6b6b59SJacob Faibussowitsch   using type = T;
5370e6b6b59SJacob Faibussowitsch };
5380e6b6b59SJacob Faibussowitsch 
5390e6b6b59SJacob Faibussowitsch template <>
5400e6b6b59SJacob Faibussowitsch struct real_type<PetscScalar> {
5410e6b6b59SJacob Faibussowitsch   using type = PetscReal;
5420e6b6b59SJacob Faibussowitsch };
5430e6b6b59SJacob Faibussowitsch 
5440e6b6b59SJacob Faibussowitsch } // namespace detail
5450e6b6b59SJacob Faibussowitsch 
5460e6b6b59SJacob Faibussowitsch template <typename T>
5470e6b6b59SJacob Faibussowitsch struct SegmentedMemoryPoolAllocatorBase {
5480e6b6b59SJacob Faibussowitsch   using value_type      = T;
5490e6b6b59SJacob Faibussowitsch   using size_type       = std::size_t;
5500e6b6b59SJacob Faibussowitsch   using real_value_type = typename detail::real_type<T>::type;
5510e6b6b59SJacob Faibussowitsch 
5520e6b6b59SJacob Faibussowitsch   template <typename U>
5530e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode allocate(value_type **, size_type, const device::StreamBase<U> *) noexcept;
5540e6b6b59SJacob Faibussowitsch   template <typename U>
5550e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode deallocate(value_type *, const device::StreamBase<U> *) noexcept;
5560e6b6b59SJacob Faibussowitsch   template <typename U>
5570e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode zero(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5580e6b6b59SJacob Faibussowitsch   template <typename U>
5590e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode uninitialized_copy(value_type *, const value_type *, size_type, const device::StreamBase<U> *) noexcept;
5600e6b6b59SJacob Faibussowitsch   template <typename U>
5610e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode set_canary(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5620e6b6b59SJacob Faibussowitsch };
5630e6b6b59SJacob Faibussowitsch 
5640e6b6b59SJacob Faibussowitsch template <typename T>
5650e6b6b59SJacob Faibussowitsch template <typename U>
5660e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::allocate(value_type **ptr, size_type n, const device::StreamBase<U> *) noexcept {
5670e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5680e6b6b59SJacob Faibussowitsch   PetscCall(PetscMalloc1(n, ptr));
5690e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5700e6b6b59SJacob Faibussowitsch }
5710e6b6b59SJacob Faibussowitsch 
5720e6b6b59SJacob Faibussowitsch template <typename T>
5730e6b6b59SJacob Faibussowitsch template <typename U>
5740e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::deallocate(value_type *ptr, const device::StreamBase<U> *) noexcept {
5750e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5760e6b6b59SJacob Faibussowitsch   PetscCall(PetscFree(ptr));
5770e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5780e6b6b59SJacob Faibussowitsch }
5790e6b6b59SJacob Faibussowitsch 
5800e6b6b59SJacob Faibussowitsch template <typename T>
5810e6b6b59SJacob Faibussowitsch template <typename U>
5820e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::zero(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept {
5830e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5840e6b6b59SJacob Faibussowitsch   PetscCall(PetscArrayzero(ptr, n));
5850e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5860e6b6b59SJacob Faibussowitsch }
5870e6b6b59SJacob Faibussowitsch 
5880e6b6b59SJacob Faibussowitsch template <typename T>
5890e6b6b59SJacob Faibussowitsch template <typename U>
5900e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::uninitialized_copy(value_type *dest, const value_type *src, size_type n, const device::StreamBase<U> *) noexcept {
5910e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5920e6b6b59SJacob Faibussowitsch   PetscCall(PetscArraycpy(dest, src, n));
5930e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5940e6b6b59SJacob Faibussowitsch }
5950e6b6b59SJacob Faibussowitsch 
5960e6b6b59SJacob Faibussowitsch template <typename T>
5970e6b6b59SJacob Faibussowitsch template <typename U>
5980e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::set_canary(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept {
5990e6b6b59SJacob Faibussowitsch   using limit_type            = std::numeric_limits<real_value_type>;
6000e6b6b59SJacob Faibussowitsch   constexpr value_type canary = limit_type::has_signaling_NaN ? limit_type::signaling_NaN() : limit_type::max();
6010e6b6b59SJacob Faibussowitsch 
6020e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6030e6b6b59SJacob Faibussowitsch   for (size_type i = 0; i < n; ++i) ptr[i] = canary;
6040e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6050e6b6b59SJacob Faibussowitsch }
6060e6b6b59SJacob Faibussowitsch 
6070e6b6b59SJacob Faibussowitsch } // namespace impl
6080e6b6b59SJacob Faibussowitsch 
6090e6b6b59SJacob Faibussowitsch // ==========================================================================================
6100e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool
611*6797ed33SJacob Faibussowitsch //
612*6797ed33SJacob Faibussowitsch // Stream-aware async memory allocator. Holds a list of memory "blocks" which each control an
613*6797ed33SJacob Faibussowitsch // allocated buffer. This buffer is further split into memory "chunks" which control
614*6797ed33SJacob Faibussowitsch // consecutive, non-overlapping regions of the block. Chunks may be in 1 of 2 states:
615*6797ed33SJacob Faibussowitsch //
616*6797ed33SJacob Faibussowitsch // 1. Open:
617*6797ed33SJacob Faibussowitsch //    The chunk is free to be claimed by the next suitable allocation request. If the
618*6797ed33SJacob Faibussowitsch //    allocation request is made on the same stream as the chunk was deallocated on, no
619*6797ed33SJacob Faibussowitsch //    serialization needs to occur. If not, the allocating stream must wait for the
620*6797ed33SJacob Faibussowitsch //    event. Claiming the chunk "closes" the chunk.
621*6797ed33SJacob Faibussowitsch //
622*6797ed33SJacob Faibussowitsch // 2. Closed:
623*6797ed33SJacob Faibussowitsch //    The chunk has been claimed by an allocation request. It cannot be opened again until it
624*6797ed33SJacob Faibussowitsch //    is deallocated; doing so "opens" the chunk.
625*6797ed33SJacob Faibussowitsch //
626*6797ed33SJacob Faibussowitsch // Note that there does not need to be a chunk for every region, chunks are created to satisfy
627*6797ed33SJacob Faibussowitsch // an allocation request.
628*6797ed33SJacob Faibussowitsch //
629*6797ed33SJacob Faibussowitsch // Thus there is usually a region of "unallocated" memory at the end of the buffer, which may
630*6797ed33SJacob Faibussowitsch // be claimed by a newly created chunk if existing chunks cannot satisfy the allocation
631*6797ed33SJacob Faibussowitsch // request. This region exists _only_ at the end, as there are no gaps between chunks.
632*6797ed33SJacob Faibussowitsch //
633*6797ed33SJacob Faibussowitsch //
634*6797ed33SJacob Faibussowitsch // |-----------------------------------------------------------------------------------------
635*6797ed33SJacob Faibussowitsch // | SegmentedMemoryPool
636*6797ed33SJacob Faibussowitsch // |
637*6797ed33SJacob Faibussowitsch // | ||-------------||
638*6797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
639*6797ed33SJacob Faibussowitsch // | ||             ||    | AAAAAAAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDXXXXXXXX...
640*6797ed33SJacob Faibussowitsch // | ||             ||    | |             |      |                   |            |
641*6797ed33SJacob Faibussowitsch // | ||             ||    | x-----x-------x-----xx---------x---------x------x-----x
642*6797ed33SJacob Faibussowitsch // | || MemoryBlock || -> | ------|-------------|----------|----------------|--------
643*6797ed33SJacob Faibussowitsch // | ||             ||    | | MemoryChunk | MemoryChunk | MemoryChunk | MemoryChunk |
644*6797ed33SJacob Faibussowitsch // | ||             ||    | ---------------------------------------------------------
645*6797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
646*6797ed33SJacob Faibussowitsch // | ||-------------||
647*6797ed33SJacob Faibussowitsch // | ||             ||
648*6797ed33SJacob Faibussowitsch // | ||     ...     ||
649*6797ed33SJacob Faibussowitsch // | ||             ||
6500e6b6b59SJacob Faibussowitsch // ==========================================================================================
6510e6b6b59SJacob Faibussowitsch 
6520e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType = device::DefaultStream, typename AllocType = impl::SegmentedMemoryPoolAllocatorBase<MemType>, std::size_t DefaultChunkSize = 256>
6530e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool;
6540e6b6b59SJacob Faibussowitsch 
6550e6b6b59SJacob Faibussowitsch // The actual memory pool class. It is in essence just a wrapper for a list of MemoryBlocks.
6560e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
6570e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool : public RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>> {
6580e6b6b59SJacob Faibussowitsch public:
6590e6b6b59SJacob Faibussowitsch   using value_type     = MemType;
6600e6b6b59SJacob Faibussowitsch   using stream_type    = StreamType;
6610e6b6b59SJacob Faibussowitsch   using allocator_type = AllocType;
6620e6b6b59SJacob Faibussowitsch   using block_type     = impl::MemoryBlock<value_type, allocator_type, stream_type>;
6630e6b6b59SJacob Faibussowitsch   using pool_type      = std::deque<block_type>;
6640e6b6b59SJacob Faibussowitsch   using size_type      = typename block_type::size_type;
6650e6b6b59SJacob Faibussowitsch 
6660e6b6b59SJacob Faibussowitsch   explicit SegmentedMemoryPool(AllocType = AllocType{}, std::size_t = DefaultChunkSize) noexcept(std::is_nothrow_default_constructible<pool_type>::value);
6670e6b6b59SJacob Faibussowitsch 
668*6797ed33SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode allocate(PetscInt, value_type **, const stream_type *, size_type = std::alignment_of<MemType>::value) noexcept;
6690e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode deallocate(value_type **, const stream_type *) noexcept;
6700e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode reallocate(PetscInt, value_type **, const stream_type *) noexcept;
6710e6b6b59SJacob Faibussowitsch 
6720e6b6b59SJacob Faibussowitsch private:
6730e6b6b59SJacob Faibussowitsch   pool_type      pool_;
6740e6b6b59SJacob Faibussowitsch   allocator_type allocator_;
6750e6b6b59SJacob Faibussowitsch   size_type      chunk_size_;
6760e6b6b59SJacob Faibussowitsch 
6770e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode make_block_(size_type, const stream_type *) noexcept;
6780e6b6b59SJacob Faibussowitsch 
6790e6b6b59SJacob Faibussowitsch   friend class RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>>;
6800e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode register_finalize_(const stream_type *) noexcept;
6810e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode finalize_() noexcept;
682*6797ed33SJacob Faibussowitsch 
683*6797ed33SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode allocate_(size_type, value_type **, const stream_type *) noexcept;
6840e6b6b59SJacob Faibussowitsch };
6850e6b6b59SJacob Faibussowitsch 
6860e6b6b59SJacob Faibussowitsch // ==========================================================================================
6870e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Private API
6880e6b6b59SJacob Faibussowitsch // ==========================================================================================
6890e6b6b59SJacob Faibussowitsch 
6900e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
6910e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::make_block_(size_type size, const stream_type *stream) noexcept {
6920e6b6b59SJacob Faibussowitsch   const auto block_size = std::max(size, chunk_size_);
6930e6b6b59SJacob Faibussowitsch 
6940e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6950e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.emplace_back(&allocator_, block_size, stream));
6960e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Allocated new block of size %zu, total %zu blocks\n", block_size, pool_.size()));
6970e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6980e6b6b59SJacob Faibussowitsch }
6990e6b6b59SJacob Faibussowitsch 
7000e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
7010e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::register_finalize_(const stream_type *stream) noexcept {
7020e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7030e6b6b59SJacob Faibussowitsch   PetscCall(make_block_(chunk_size_, stream));
7040e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7050e6b6b59SJacob Faibussowitsch }
7060e6b6b59SJacob Faibussowitsch 
7070e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
7080e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::finalize_() noexcept {
7090e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7100e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.clear());
7110e6b6b59SJacob Faibussowitsch   chunk_size_ = DefaultChunkSize;
7120e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7130e6b6b59SJacob Faibussowitsch }
7140e6b6b59SJacob Faibussowitsch 
715*6797ed33SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
716*6797ed33SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate_(size_type size, value_type **ptr, const stream_type *stream) noexcept {
717*6797ed33SJacob Faibussowitsch   auto found = false;
718*6797ed33SJacob Faibussowitsch 
719*6797ed33SJacob Faibussowitsch   PetscFunctionBegin;
720*6797ed33SJacob Faibussowitsch   PetscCall(this->register_finalize(PETSC_COMM_SELF, stream));
721*6797ed33SJacob Faibussowitsch   for (auto &block : pool_) {
722*6797ed33SJacob Faibussowitsch     PetscCall(block.try_allocate_chunk(size, ptr, stream, &found));
723*6797ed33SJacob Faibussowitsch     if (PetscLikely(found)) PetscFunctionReturn(0);
724*6797ed33SJacob Faibussowitsch   }
725*6797ed33SJacob Faibussowitsch 
726*6797ed33SJacob 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));
727*6797ed33SJacob Faibussowitsch   // if we are here we couldn't find an open block in the pool, so make a new block
728*6797ed33SJacob Faibussowitsch   PetscCall(make_block_(size, stream));
729*6797ed33SJacob Faibussowitsch   // and assign it
730*6797ed33SJacob Faibussowitsch   PetscCall(pool_.back().try_allocate_chunk(size, ptr, stream, &found));
731*6797ed33SJacob 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());
732*6797ed33SJacob Faibussowitsch   PetscFunctionReturn(0);
733*6797ed33SJacob Faibussowitsch }
734*6797ed33SJacob Faibussowitsch 
7350e6b6b59SJacob Faibussowitsch // ==========================================================================================
7360e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Public API
7370e6b6b59SJacob Faibussowitsch // ==========================================================================================
7380e6b6b59SJacob Faibussowitsch 
7390e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
7400e6b6b59SJacob Faibussowitsch inline SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::SegmentedMemoryPool(AllocType alloc, std::size_t size) noexcept(std::is_nothrow_default_constructible<pool_type>::value) :
7410e6b6b59SJacob Faibussowitsch   allocator_(std::move(alloc)), chunk_size_(size) { }
7420e6b6b59SJacob Faibussowitsch 
7430e6b6b59SJacob Faibussowitsch /*
7440e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::allocate - get an allocation from the memory pool
7450e6b6b59SJacob Faibussowitsch 
7460e6b6b59SJacob Faibussowitsch   Input Parameters:
7470e6b6b59SJacob Faibussowitsch + req_size - size (in elements) to get
7480e6b6b59SJacob Faibussowitsch . ptr      - the pointer to hold the allocation
7490e6b6b59SJacob Faibussowitsch - stream   - the stream on which to get the allocation
7500e6b6b59SJacob Faibussowitsch 
7510e6b6b59SJacob Faibussowitsch   Output Parameter:
7520e6b6b59SJacob Faibussowitsch . ptr - the pointer holding the allocation
7530e6b6b59SJacob Faibussowitsch 
7540e6b6b59SJacob Faibussowitsch   Notes:
7550e6b6b59SJacob Faibussowitsch   req_size cannot be negative. If req_size if zero, ptr is set to nullptr
7560e6b6b59SJacob Faibussowitsch */
7570e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
758*6797ed33SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate(PetscInt req_size, value_type **ptr, const stream_type *stream, size_type alignment) noexcept {
759*6797ed33SJacob Faibussowitsch   value_type *ret_ptr = nullptr;
7600e6b6b59SJacob Faibussowitsch 
7610e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7620e6b6b59SJacob Faibussowitsch   PetscAssert(req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", req_size);
7630e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 2);
7640e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 3);
765*6797ed33SJacob Faibussowitsch   if (req_size) {
766*6797ed33SJacob Faibussowitsch     const auto size         = static_cast<size_type>(req_size);
767*6797ed33SJacob Faibussowitsch     auto       aligned_size = alignment == alignof(char) ? size : size + alignment;
768*6797ed33SJacob Faibussowitsch     void      *vptr         = nullptr;
7690e6b6b59SJacob Faibussowitsch 
770*6797ed33SJacob Faibussowitsch     PetscCall(allocate_(aligned_size, &ret_ptr, stream));
771*6797ed33SJacob Faibussowitsch     vptr = ret_ptr;
772*6797ed33SJacob Faibussowitsch     std::align(alignment, size, vptr, aligned_size);
773*6797ed33SJacob Faibussowitsch     ret_ptr = reinterpret_cast<value_type *>(vptr);
774*6797ed33SJacob Faibussowitsch     // sets memory to NaN or infinity depending on the type to catch out uninitialized memory
775*6797ed33SJacob Faibussowitsch     // accesses.
776*6797ed33SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) PetscCall(allocator_.set_canary(ret_ptr, size, stream));
777*6797ed33SJacob Faibussowitsch   }
778*6797ed33SJacob Faibussowitsch   *ptr = ret_ptr;
7790e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7800e6b6b59SJacob Faibussowitsch }
7810e6b6b59SJacob Faibussowitsch 
7820e6b6b59SJacob Faibussowitsch /*
7830e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::deallocate - release a pointer back to the memory pool
7840e6b6b59SJacob Faibussowitsch 
7850e6b6b59SJacob Faibussowitsch   Input Parameters:
7860e6b6b59SJacob Faibussowitsch + ptr    - the pointer to release
7870e6b6b59SJacob Faibussowitsch - stream - the stream to release it on
7880e6b6b59SJacob Faibussowitsch 
7890e6b6b59SJacob Faibussowitsch   Notes:
7900e6b6b59SJacob Faibussowitsch   If ptr is not owned by the pool it is unchanged.
7910e6b6b59SJacob Faibussowitsch */
7920e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
7930e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::deallocate(value_type **ptr, const stream_type *stream) noexcept {
7940e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7950e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 1);
7960e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 2);
7970e6b6b59SJacob Faibussowitsch   // nobody owns a nullptr, and if they do then they have bigger problems
7980e6b6b59SJacob Faibussowitsch   if (!*ptr) PetscFunctionReturn(0);
7990e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8000e6b6b59SJacob Faibussowitsch     auto found = false;
8010e6b6b59SJacob Faibussowitsch 
8020e6b6b59SJacob Faibussowitsch     PetscCall(block.try_deallocate_chunk(ptr, stream, &found));
8030e6b6b59SJacob Faibussowitsch     if (PetscLikely(found)) break;
8040e6b6b59SJacob Faibussowitsch   }
8050e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
8060e6b6b59SJacob Faibussowitsch }
8070e6b6b59SJacob Faibussowitsch 
8080e6b6b59SJacob Faibussowitsch /*
8090e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::reallocate - Resize an allocated buffer
8100e6b6b59SJacob Faibussowitsch 
8110e6b6b59SJacob Faibussowitsch   Input Parameters:
8120e6b6b59SJacob Faibussowitsch + new_req_size - the new buffer size
8130e6b6b59SJacob Faibussowitsch . ptr          - pointer to the buffer
8140e6b6b59SJacob Faibussowitsch - stream       - stream to resize with
8150e6b6b59SJacob Faibussowitsch 
8160e6b6b59SJacob Faibussowitsch   Ouput Parameter:
8170e6b6b59SJacob Faibussowitsch . ptr - pointer to the new region
8180e6b6b59SJacob Faibussowitsch 
8190e6b6b59SJacob Faibussowitsch   Notes:
8200e6b6b59SJacob Faibussowitsch   ptr must have been allocated by the pool.
8210e6b6b59SJacob Faibussowitsch 
8220e6b6b59SJacob Faibussowitsch   It's OK to shrink the buffer, even down to 0 (in which case it is just deallocated).
8230e6b6b59SJacob Faibussowitsch */
8240e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
8250e6b6b59SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::reallocate(PetscInt new_req_size, value_type **ptr, const stream_type *stream) noexcept {
8260e6b6b59SJacob Faibussowitsch   using chunk_type = typename block_type::chunk_type;
8270e6b6b59SJacob Faibussowitsch 
8280e6b6b59SJacob Faibussowitsch   const auto  new_size = static_cast<size_type>(new_req_size);
8290e6b6b59SJacob Faibussowitsch   const auto  old_ptr  = *ptr;
8300e6b6b59SJacob Faibussowitsch   chunk_type *chunk    = nullptr;
8310e6b6b59SJacob Faibussowitsch 
8320e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8330e6b6b59SJacob Faibussowitsch   PetscAssert(new_req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", new_req_size);
8340e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 2);
8350e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 3);
8360e6b6b59SJacob Faibussowitsch 
8370e6b6b59SJacob Faibussowitsch   // if reallocating to zero, just free
8380e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(new_size == 0)) {
8390e6b6b59SJacob Faibussowitsch     PetscCall(deallocate(ptr, stream));
8400e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
8410e6b6b59SJacob Faibussowitsch   }
8420e6b6b59SJacob Faibussowitsch 
8430e6b6b59SJacob Faibussowitsch   // search the blocks for the owning chunk
8440e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8450e6b6b59SJacob Faibussowitsch     PetscCall(block.try_find_chunk(old_ptr, &chunk));
8460e6b6b59SJacob Faibussowitsch     if (chunk) break; // found
8470e6b6b59SJacob Faibussowitsch   }
8480e6b6b59SJacob Faibussowitsch   PetscAssert(chunk, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Memory pool does not own %p, so cannot reallocate it", *ptr);
8490e6b6b59SJacob Faibussowitsch 
8500e6b6b59SJacob Faibussowitsch   if (chunk->capacity() < new_size) {
8510e6b6b59SJacob Faibussowitsch     // chunk does not have enough room, need to grab a fresh chunk and copy to it
8520e6b6b59SJacob Faibussowitsch     *ptr = nullptr;
8530e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
8540e6b6b59SJacob Faibussowitsch     PetscCall(allocate(new_size, ptr, stream));
8550e6b6b59SJacob Faibussowitsch     PetscCall(allocator_.uninitialized_copy(*ptr, old_ptr, new_size, stream));
8560e6b6b59SJacob Faibussowitsch   } else {
8570e6b6b59SJacob Faibussowitsch     // chunk had enough room we can simply grow (or shrink) to fit the new size
8580e6b6b59SJacob Faibussowitsch     PetscCall(chunk->resize(new_size));
8590e6b6b59SJacob Faibussowitsch   }
8600e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
8610e6b6b59SJacob Faibussowitsch }
8620e6b6b59SJacob Faibussowitsch 
8630e6b6b59SJacob Faibussowitsch } // namespace memory
8640e6b6b59SJacob Faibussowitsch 
8650e6b6b59SJacob Faibussowitsch } // namespace Petsc
8660e6b6b59SJacob Faibussowitsch 
8670e6b6b59SJacob Faibussowitsch #endif // PETSC_SEGMENTEDMEMPOOL_HPP
868