xref: /petsc/src/sys/objects/device/impls/segmentedmempool.hpp (revision 503aa7ef0aa2ab69e19354d096ea496a9a457d08)
10e6b6b59SJacob Faibussowitsch #ifndef PETSC_SEGMENTEDMEMPOOL_HPP
20e6b6b59SJacob Faibussowitsch #define PETSC_SEGMENTEDMEMPOOL_HPP
30e6b6b59SJacob Faibussowitsch 
40e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h>
56797ed33SJacob 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>
106797ed33SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp>
110e6b6b59SJacob Faibussowitsch 
120e6b6b59SJacob Faibussowitsch #include <limits>
130e6b6b59SJacob Faibussowitsch #include <deque>
140e6b6b59SJacob Faibussowitsch #include <vector>
150e6b6b59SJacob Faibussowitsch 
16d71ae5a4SJacob Faibussowitsch namespace Petsc
17d71ae5a4SJacob Faibussowitsch {
180e6b6b59SJacob Faibussowitsch 
19d71ae5a4SJacob Faibussowitsch namespace device
20d71ae5a4SJacob Faibussowitsch {
210e6b6b59SJacob Faibussowitsch 
220e6b6b59SJacob Faibussowitsch template <typename T>
230e6b6b59SJacob Faibussowitsch class StreamBase {
240e6b6b59SJacob Faibussowitsch public:
250e6b6b59SJacob Faibussowitsch   using id_type      = int;
260e6b6b59SJacob Faibussowitsch   using derived_type = T;
270e6b6b59SJacob Faibussowitsch 
280e6b6b59SJacob Faibussowitsch   static const id_type INVALID_ID;
290e6b6b59SJacob Faibussowitsch 
300e6b6b59SJacob Faibussowitsch   // needed so that dependent auto works, see veccupmimpl.h for a detailed discussion
310e6b6b59SJacob Faibussowitsch   template <typename U = T>
320e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD auto get_stream() const noexcept PETSC_DECLTYPE_AUTO_RETURNS(static_cast<const U &>(*this).get_stream_());
330e6b6b59SJacob Faibussowitsch 
340e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD id_type get_id() const noexcept { return static_cast<const T &>(*this).get_id_(); }
350e6b6b59SJacob Faibussowitsch 
360e6b6b59SJacob Faibussowitsch   template <typename E>
37d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode record_event(E &&event) const noexcept
38d71ae5a4SJacob Faibussowitsch   {
390e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).record_event_(std::forward<E>(event));
400e6b6b59SJacob Faibussowitsch   }
410e6b6b59SJacob Faibussowitsch 
420e6b6b59SJacob Faibussowitsch   template <typename E>
43d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode wait_for_event(E &&event) const noexcept
44d71ae5a4SJacob Faibussowitsch   {
450e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).wait_for_(std::forward<E>(event));
460e6b6b59SJacob Faibussowitsch   }
470e6b6b59SJacob Faibussowitsch 
480e6b6b59SJacob Faibussowitsch protected:
490e6b6b59SJacob Faibussowitsch   constexpr StreamBase() noexcept = default;
500e6b6b59SJacob Faibussowitsch 
510e6b6b59SJacob Faibussowitsch   struct default_event_type { };
520e6b6b59SJacob Faibussowitsch   using default_stream_type = std::nullptr_t;
530e6b6b59SJacob Faibussowitsch 
540e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr default_stream_type get_stream_() noexcept { return nullptr; }
550e6b6b59SJacob Faibussowitsch 
560e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr id_type get_id_() noexcept { return 0; }
570e6b6b59SJacob Faibussowitsch 
580e6b6b59SJacob Faibussowitsch   template <typename U = T>
59d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD static constexpr PetscErrorCode record_event_(const typename U::event_type &) noexcept
60d71ae5a4SJacob Faibussowitsch   {
610e6b6b59SJacob Faibussowitsch     return 0;
620e6b6b59SJacob Faibussowitsch   }
630e6b6b59SJacob Faibussowitsch 
640e6b6b59SJacob Faibussowitsch   template <typename U = T>
65d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD static constexpr PetscErrorCode wait_for_(const typename U::event_type &) noexcept
66d71ae5a4SJacob Faibussowitsch   {
670e6b6b59SJacob Faibussowitsch     return 0;
680e6b6b59SJacob Faibussowitsch   }
690e6b6b59SJacob Faibussowitsch };
700e6b6b59SJacob Faibussowitsch 
710e6b6b59SJacob Faibussowitsch template <typename T>
720e6b6b59SJacob Faibussowitsch const typename StreamBase<T>::id_type StreamBase<T>::INVALID_ID = -1;
730e6b6b59SJacob Faibussowitsch 
740e6b6b59SJacob Faibussowitsch struct DefaultStream : StreamBase<DefaultStream> {
750e6b6b59SJacob Faibussowitsch   using stream_type = typename StreamBase<DefaultStream>::default_stream_type;
760e6b6b59SJacob Faibussowitsch   using id_type     = typename StreamBase<DefaultStream>::id_type;
770e6b6b59SJacob Faibussowitsch   using event_type  = typename StreamBase<DefaultStream>::default_event_type;
780e6b6b59SJacob Faibussowitsch };
790e6b6b59SJacob Faibussowitsch 
800e6b6b59SJacob Faibussowitsch } // namespace device
810e6b6b59SJacob Faibussowitsch 
82d71ae5a4SJacob Faibussowitsch namespace memory
83d71ae5a4SJacob Faibussowitsch {
840e6b6b59SJacob Faibussowitsch 
85d71ae5a4SJacob Faibussowitsch namespace impl
86d71ae5a4SJacob Faibussowitsch {
870e6b6b59SJacob Faibussowitsch 
880e6b6b59SJacob Faibussowitsch // ==========================================================================================
890e6b6b59SJacob Faibussowitsch // MemoryChunk
900e6b6b59SJacob Faibussowitsch //
910e6b6b59SJacob Faibussowitsch // Represents a checked-out region of a MemoryBlock. Tracks the offset into the owning
920e6b6b59SJacob Faibussowitsch // MemoryBlock and its size/capacity
930e6b6b59SJacob Faibussowitsch // ==========================================================================================
940e6b6b59SJacob Faibussowitsch 
950e6b6b59SJacob Faibussowitsch template <typename EventType>
960e6b6b59SJacob Faibussowitsch class MemoryChunk {
970e6b6b59SJacob Faibussowitsch public:
980e6b6b59SJacob Faibussowitsch   using event_type = EventType;
990e6b6b59SJacob Faibussowitsch   using size_type  = std::size_t;
1000e6b6b59SJacob Faibussowitsch 
1010e6b6b59SJacob Faibussowitsch   MemoryChunk(size_type, size_type) noexcept;
1020e6b6b59SJacob Faibussowitsch   explicit MemoryChunk(size_type) noexcept;
1030e6b6b59SJacob Faibussowitsch 
1040e6b6b59SJacob Faibussowitsch   MemoryChunk(MemoryChunk &&) noexcept;
1050e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(MemoryChunk &&) noexcept;
1060e6b6b59SJacob Faibussowitsch 
1070e6b6b59SJacob Faibussowitsch   MemoryChunk(const MemoryChunk &) noexcept            = delete;
1080e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(const MemoryChunk &) noexcept = delete;
1090e6b6b59SJacob Faibussowitsch 
1100e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type start() const noexcept { return start_; }
1110e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
1120e6b6b59SJacob Faibussowitsch   // REVIEW ME:
1130e6b6b59SJacob Faibussowitsch   // make this an actual field, normally each chunk shrinks_to_fit() on begin claimed, but in
1140e6b6b59SJacob Faibussowitsch   // theory only the last chunk needs to do this
1150e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type capacity() const noexcept { return size_; }
1160e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type total_offset() const noexcept { return start() + size(); }
1170e6b6b59SJacob Faibussowitsch 
1180e6b6b59SJacob Faibussowitsch   template <typename U>
1190e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode release(const device::StreamBase<U> *) noexcept;
1200e6b6b59SJacob Faibussowitsch   template <typename U>
1210e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode claim(const device::StreamBase<U> *, size_type, bool *, bool = false) noexcept;
1220e6b6b59SJacob Faibussowitsch   template <typename U>
1230e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool           can_claim(const device::StreamBase<U> *, size_type, bool) const noexcept;
1240e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode resize(size_type) noexcept;
1256797ed33SJacob Faibussowitsch   PETSC_NODISCARD bool           contains(size_type) const noexcept;
1260e6b6b59SJacob Faibussowitsch 
1270e6b6b59SJacob Faibussowitsch private:
1280e6b6b59SJacob Faibussowitsch   // clang-format off
1290e6b6b59SJacob Faibussowitsch   event_type      event_{};          // event recorded when the chunk was released
1300e6b6b59SJacob Faibussowitsch   bool            open_      = true; // is this chunk open?
1310e6b6b59SJacob Faibussowitsch   // id of the last stream to use the chunk, populated on release
1320e6b6b59SJacob Faibussowitsch   int             stream_id_ = device::DefaultStream::INVALID_ID;
1330e6b6b59SJacob Faibussowitsch   size_type       size_      = 0;    // size of the chunk
1340e6b6b59SJacob Faibussowitsch   const size_type start_     = 0;    // offset from the start of the owning block
1350e6b6b59SJacob Faibussowitsch   // clang-format on
1366797ed33SJacob Faibussowitsch 
1370e6b6b59SJacob Faibussowitsch   template <typename U>
1380e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool stream_compat_(const device::StreamBase<U> *) const noexcept;
1390e6b6b59SJacob Faibussowitsch };
1400e6b6b59SJacob Faibussowitsch 
1410e6b6b59SJacob Faibussowitsch // ==========================================================================================
1420e6b6b59SJacob Faibussowitsch // MemoryChunk - Private API
1430e6b6b59SJacob Faibussowitsch // ==========================================================================================
1440e6b6b59SJacob Faibussowitsch 
1450e6b6b59SJacob Faibussowitsch // asks and answers the question: can this stream claim this chunk without serializing?
1460e6b6b59SJacob Faibussowitsch template <typename E>
1470e6b6b59SJacob Faibussowitsch template <typename U>
148d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::stream_compat_(const device::StreamBase<U> *strm) const noexcept
149d71ae5a4SJacob Faibussowitsch {
1500e6b6b59SJacob Faibussowitsch   return (stream_id_ == strm->INVALID_ID) || (stream_id_ == strm->get_id());
1510e6b6b59SJacob Faibussowitsch }
1520e6b6b59SJacob Faibussowitsch 
1530e6b6b59SJacob Faibussowitsch // ==========================================================================================
1540e6b6b59SJacob Faibussowitsch // MemoryChunk - Public API
1550e6b6b59SJacob Faibussowitsch // ==========================================================================================
1560e6b6b59SJacob Faibussowitsch 
1570e6b6b59SJacob Faibussowitsch template <typename E>
158d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type start, size_type size) noexcept : size_(size), start_(start)
159d71ae5a4SJacob Faibussowitsch {
160d71ae5a4SJacob Faibussowitsch }
1610e6b6b59SJacob Faibussowitsch 
1620e6b6b59SJacob Faibussowitsch template <typename E>
163d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type size) noexcept : MemoryChunk(0, size)
164d71ae5a4SJacob Faibussowitsch {
165d71ae5a4SJacob Faibussowitsch }
1660e6b6b59SJacob Faibussowitsch 
1670e6b6b59SJacob Faibussowitsch template <typename E>
1680e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(MemoryChunk<E> &&other) noexcept :
169d71ae5a4SJacob 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_))
170d71ae5a4SJacob Faibussowitsch {
171d71ae5a4SJacob Faibussowitsch }
1720e6b6b59SJacob Faibussowitsch 
1730e6b6b59SJacob Faibussowitsch template <typename E>
174d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E> &MemoryChunk<E>::operator=(MemoryChunk<E> &&other) noexcept
175d71ae5a4SJacob Faibussowitsch {
1760e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1770e6b6b59SJacob Faibussowitsch   if (this != &other) {
1780e6b6b59SJacob Faibussowitsch     event_     = std::move(other.event_);
1790e6b6b59SJacob Faibussowitsch     open_      = util::exchange(other.open_, false);
1800e6b6b59SJacob Faibussowitsch     stream_id_ = util::exchange(other.stream_id_, device::DefaultStream::INVALID_ID);
1810e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
1820e6b6b59SJacob Faibussowitsch     start_     = std::move(other.start_);
1830e6b6b59SJacob Faibussowitsch   }
1840e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
1850e6b6b59SJacob Faibussowitsch }
1860e6b6b59SJacob Faibussowitsch 
1870e6b6b59SJacob Faibussowitsch /*
1880e6b6b59SJacob Faibussowitsch   MemoryChunk::release - release a chunk on a stream
1890e6b6b59SJacob Faibussowitsch 
1900e6b6b59SJacob Faibussowitsch   Input Parameter:
1910e6b6b59SJacob Faibussowitsch . stream - the stream to release the chunk with
1920e6b6b59SJacob Faibussowitsch 
1930e6b6b59SJacob Faibussowitsch   Notes:
1940e6b6b59SJacob Faibussowitsch   Inserts a release operation on stream and records the state of stream at the time this
1950e6b6b59SJacob Faibussowitsch   routine was called.
1960e6b6b59SJacob Faibussowitsch 
1970e6b6b59SJacob Faibussowitsch   Future allocation requests which attempt to claim the chunk on the same stream may re-acquire
1980e6b6b59SJacob Faibussowitsch   the chunk without serialization.
1990e6b6b59SJacob Faibussowitsch 
2000e6b6b59SJacob Faibussowitsch   If another stream attempts to claim the chunk they must wait for the recorded event before
2010e6b6b59SJacob Faibussowitsch   claiming the chunk.
2020e6b6b59SJacob Faibussowitsch */
2030e6b6b59SJacob Faibussowitsch template <typename E>
2040e6b6b59SJacob Faibussowitsch template <typename U>
205d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::release(const device::StreamBase<U> *stream) noexcept
206d71ae5a4SJacob Faibussowitsch {
2070e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2080e6b6b59SJacob Faibussowitsch   open_      = true;
2090e6b6b59SJacob Faibussowitsch   stream_id_ = stream->get_id();
2100e6b6b59SJacob Faibussowitsch   PetscCall(stream->record_event(event_));
2110e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2120e6b6b59SJacob Faibussowitsch }
2130e6b6b59SJacob Faibussowitsch 
2140e6b6b59SJacob Faibussowitsch /*
2150e6b6b59SJacob Faibussowitsch   MemoryChunk::claim - attempt to claim a particular chunk
2160e6b6b59SJacob Faibussowitsch 
2170e6b6b59SJacob Faibussowitsch   Input Parameters:
2180e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2190e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2200e6b6b59SJacob Faibussowitsch - serialize - (optional, false) whether the claimant allows serialization
2210e6b6b59SJacob Faibussowitsch 
2220e6b6b59SJacob Faibussowitsch   Output Parameter:
2230e6b6b59SJacob Faibussowitsch . success - true if the chunk was claimed, false otherwise
2240e6b6b59SJacob Faibussowitsch */
2250e6b6b59SJacob Faibussowitsch template <typename E>
2260e6b6b59SJacob Faibussowitsch template <typename U>
227d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::claim(const device::StreamBase<U> *stream, size_type req_size, bool *success, bool serialize) noexcept
228d71ae5a4SJacob Faibussowitsch {
2290e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2300e6b6b59SJacob Faibussowitsch   if ((*success = can_claim(stream, req_size, serialize))) {
2310e6b6b59SJacob Faibussowitsch     if (serialize && !stream_compat_(stream)) PetscCall(stream->wait_for_event(event_));
2320e6b6b59SJacob Faibussowitsch     PetscCall(resize(req_size));
2330e6b6b59SJacob Faibussowitsch     open_ = false;
2340e6b6b59SJacob Faibussowitsch   }
2350e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2360e6b6b59SJacob Faibussowitsch }
2370e6b6b59SJacob Faibussowitsch 
2380e6b6b59SJacob Faibussowitsch /*
2390e6b6b59SJacob Faibussowitsch   MemoryChunk::can_claim - test whether a particular chunk can be claimed
2400e6b6b59SJacob Faibussowitsch 
2410e6b6b59SJacob Faibussowitsch   Input Parameters:
2420e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2430e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2440e6b6b59SJacob Faibussowitsch - serialize - whether the claimant allows serialization
2450e6b6b59SJacob Faibussowitsch 
2460e6b6b59SJacob Faibussowitsch   Output:
2470e6b6b59SJacob Faibussowitsch . [return] - true if the chunk is claimable given the configuration, false otherwise
2480e6b6b59SJacob Faibussowitsch */
2490e6b6b59SJacob Faibussowitsch template <typename E>
2500e6b6b59SJacob Faibussowitsch template <typename U>
251d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::can_claim(const device::StreamBase<U> *stream, size_type req_size, bool serialize) const noexcept
252d71ae5a4SJacob Faibussowitsch {
2530e6b6b59SJacob Faibussowitsch   if (open_ && (req_size <= capacity())) {
2540e6b6b59SJacob Faibussowitsch     // fully compatible
2550e6b6b59SJacob Faibussowitsch     if (stream_compat_(stream)) return true;
2560e6b6b59SJacob Faibussowitsch     // stream wasn't compatible, but could claim if we serialized
2570e6b6b59SJacob Faibussowitsch     if (serialize) return true;
2580e6b6b59SJacob Faibussowitsch     // incompatible stream and did not want to serialize
2590e6b6b59SJacob Faibussowitsch   }
2600e6b6b59SJacob Faibussowitsch   return false;
2610e6b6b59SJacob Faibussowitsch }
2620e6b6b59SJacob Faibussowitsch 
2630e6b6b59SJacob Faibussowitsch /*
2640e6b6b59SJacob Faibussowitsch   MemoryChunk::resize - grow a chunk to new size
2650e6b6b59SJacob Faibussowitsch 
2660e6b6b59SJacob Faibussowitsch   Input Parameter:
2670e6b6b59SJacob Faibussowitsch . newsize - the new size Requested
2680e6b6b59SJacob Faibussowitsch 
2690e6b6b59SJacob Faibussowitsch   Notes:
2700e6b6b59SJacob Faibussowitsch   newsize cannot be larger than capacity
2710e6b6b59SJacob Faibussowitsch */
2720e6b6b59SJacob Faibussowitsch template <typename E>
273d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::resize(size_type newsize) noexcept
274d71ae5a4SJacob Faibussowitsch {
2750e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2760e6b6b59SJacob Faibussowitsch   PetscAssert(newsize <= capacity(), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "New size %zu larger than capacity %zu", newsize, capacity());
2770e6b6b59SJacob Faibussowitsch   size_ = newsize;
2780e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
2790e6b6b59SJacob Faibussowitsch }
2800e6b6b59SJacob Faibussowitsch 
2816797ed33SJacob Faibussowitsch /*
2826797ed33SJacob Faibussowitsch   MemoryChunk::contains - query whether a memory chunk contains a particular offset
2836797ed33SJacob Faibussowitsch 
2846797ed33SJacob Faibussowitsch   Input Parameters:
2856797ed33SJacob Faibussowitsch . offset - The offset from the MemoryBlock start
2866797ed33SJacob Faibussowitsch 
2876797ed33SJacob Faibussowitsch   Notes:
2886797ed33SJacob Faibussowitsch   Returns true if the chunk contains the offset, false otherwise
2896797ed33SJacob Faibussowitsch */
2906797ed33SJacob Faibussowitsch template <typename E>
291d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::contains(size_type offset) const noexcept
292d71ae5a4SJacob Faibussowitsch {
2936797ed33SJacob Faibussowitsch   return (offset >= start()) && (offset < total_offset());
2946797ed33SJacob Faibussowitsch }
2956797ed33SJacob Faibussowitsch 
2960e6b6b59SJacob Faibussowitsch // ==========================================================================================
2970e6b6b59SJacob Faibussowitsch // MemoryBlock
2980e6b6b59SJacob Faibussowitsch //
2990e6b6b59SJacob Faibussowitsch // A "memory block" manager, which owns the pointer to a particular memory range. Retrieving
3000e6b6b59SJacob Faibussowitsch // and restoring a block is thread-safe (so may be used by multiple device streams).
3010e6b6b59SJacob Faibussowitsch // ==========================================================================================
3020e6b6b59SJacob Faibussowitsch 
3030e6b6b59SJacob Faibussowitsch template <typename T, typename AllocatorType, typename StreamType>
3040e6b6b59SJacob Faibussowitsch class MemoryBlock {
3050e6b6b59SJacob Faibussowitsch public:
3060e6b6b59SJacob Faibussowitsch   using value_type      = T;
3070e6b6b59SJacob Faibussowitsch   using allocator_type  = AllocatorType;
3080e6b6b59SJacob Faibussowitsch   using stream_type     = StreamType;
3090e6b6b59SJacob Faibussowitsch   using event_type      = typename stream_type::event_type;
3100e6b6b59SJacob Faibussowitsch   using chunk_type      = MemoryChunk<event_type>;
3110e6b6b59SJacob Faibussowitsch   using size_type       = typename chunk_type::size_type;
3120e6b6b59SJacob Faibussowitsch   using chunk_list_type = std::vector<chunk_type>;
3130e6b6b59SJacob Faibussowitsch 
3140e6b6b59SJacob Faibussowitsch   template <typename U>
3150e6b6b59SJacob Faibussowitsch   MemoryBlock(allocator_type *, size_type, const device::StreamBase<U> *) noexcept;
3160e6b6b59SJacob Faibussowitsch 
3170e6b6b59SJacob Faibussowitsch   ~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value);
3180e6b6b59SJacob Faibussowitsch 
3190e6b6b59SJacob Faibussowitsch   MemoryBlock(MemoryBlock &&) noexcept;
3200e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(MemoryBlock &&) noexcept;
3210e6b6b59SJacob Faibussowitsch 
3220e6b6b59SJacob Faibussowitsch   // memory blocks are not copyable
3230e6b6b59SJacob Faibussowitsch   MemoryBlock(const MemoryBlock &)            = delete;
3240e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(const MemoryBlock &) = delete;
3250e6b6b59SJacob Faibussowitsch 
3260e6b6b59SJacob Faibussowitsch   /* --- actual functions --- */
3270e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_allocate_chunk(size_type, T **, const stream_type *, bool *) noexcept;
3280e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_deallocate_chunk(T **, const stream_type *, bool *) noexcept;
3290e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode try_find_chunk(const T *, chunk_type **) noexcept;
3300e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool           owns_pointer(const T *) const noexcept;
3310e6b6b59SJacob Faibussowitsch 
3320e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
3330e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type bytes() const noexcept { return sizeof(value_type) * size(); }
3340e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type num_chunks() const noexcept { return chunks_.size(); }
3350e6b6b59SJacob Faibussowitsch 
3360e6b6b59SJacob Faibussowitsch private:
3370e6b6b59SJacob Faibussowitsch   value_type     *mem_{};
3380e6b6b59SJacob Faibussowitsch   allocator_type *allocator_{};
3390e6b6b59SJacob Faibussowitsch   size_type       size_{};
3400e6b6b59SJacob Faibussowitsch   chunk_list_type chunks_{};
3410e6b6b59SJacob Faibussowitsch 
3420e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode clear_(const stream_type *) noexcept;
3430e6b6b59SJacob Faibussowitsch };
3440e6b6b59SJacob Faibussowitsch 
3450e6b6b59SJacob Faibussowitsch // ==========================================================================================
3460e6b6b59SJacob Faibussowitsch // MemoryBlock - Private API
3470e6b6b59SJacob Faibussowitsch // ==========================================================================================
3480e6b6b59SJacob Faibussowitsch 
3490e6b6b59SJacob Faibussowitsch // clear the memory block, called from destructors and move assignment/construction
3500e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
351d71ae5a4SJacob Faibussowitsch PETSC_NODISCARD PetscErrorCode MemoryBlock<T, A, S>::clear_(const stream_type *stream) noexcept
352d71ae5a4SJacob Faibussowitsch {
3530e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3540e6b6b59SJacob Faibussowitsch   if (PetscLikely(mem_)) {
3550e6b6b59SJacob Faibussowitsch     PetscCall(allocator_->deallocate(mem_, stream));
3560e6b6b59SJacob Faibussowitsch     mem_ = nullptr;
3570e6b6b59SJacob Faibussowitsch   }
3580e6b6b59SJacob Faibussowitsch   size_ = 0;
3590e6b6b59SJacob Faibussowitsch   PetscCallCXX(chunks_.clear());
3600e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
3610e6b6b59SJacob Faibussowitsch }
3620e6b6b59SJacob Faibussowitsch 
3630e6b6b59SJacob Faibussowitsch // ==========================================================================================
3640e6b6b59SJacob Faibussowitsch // MemoryBlock - Public API
3650e6b6b59SJacob Faibussowitsch // ==========================================================================================
3660e6b6b59SJacob Faibussowitsch 
3670e6b6b59SJacob Faibussowitsch // default constructor, allocates memory immediately
3680e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3690e6b6b59SJacob Faibussowitsch template <typename U>
370d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::MemoryBlock(allocator_type *alloc, size_type s, const device::StreamBase<U> *stream) noexcept : allocator_(alloc), size_(s)
371d71ae5a4SJacob Faibussowitsch {
3720e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3730e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, alloc->allocate(&mem_, s, stream));
3740e6b6b59SJacob Faibussowitsch   PetscAssertAbort(mem_, PETSC_COMM_SELF, PETSC_ERR_MEM, "Failed to allocate memory block of size %zu", s);
3750e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3760e6b6b59SJacob Faibussowitsch }
3770e6b6b59SJacob Faibussowitsch 
3780e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
379d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value)
380d71ae5a4SJacob Faibussowitsch {
3810e6b6b59SJacob Faibussowitsch   stream_type stream;
3820e6b6b59SJacob Faibussowitsch 
3830e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3840e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
3850e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3860e6b6b59SJacob Faibussowitsch }
3870e6b6b59SJacob Faibussowitsch 
3880e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
389d71ae5a4SJacob 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_))
390d71ae5a4SJacob Faibussowitsch {
391d71ae5a4SJacob Faibussowitsch }
3920e6b6b59SJacob Faibussowitsch 
3930e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
394d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S> &MemoryBlock<T, A, S>::operator=(MemoryBlock &&other) noexcept
395d71ae5a4SJacob Faibussowitsch {
3960e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3970e6b6b59SJacob Faibussowitsch   if (this != &other) {
3980e6b6b59SJacob Faibussowitsch     stream_type stream;
3990e6b6b59SJacob Faibussowitsch 
4000e6b6b59SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
4010e6b6b59SJacob Faibussowitsch     mem_       = util::exchange(other.mem_, nullptr);
4020e6b6b59SJacob Faibussowitsch     allocator_ = other.allocator_;
4030e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
4040e6b6b59SJacob Faibussowitsch     chunks_    = std::move(other.chunks_);
4050e6b6b59SJacob Faibussowitsch   }
4060e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
4070e6b6b59SJacob Faibussowitsch }
4080e6b6b59SJacob Faibussowitsch 
4090e6b6b59SJacob Faibussowitsch /*
4100e6b6b59SJacob Faibussowitsch   MemoryBock::owns_pointer - returns true if this block owns a pointer, false otherwise
4110e6b6b59SJacob Faibussowitsch */
4120e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
413d71ae5a4SJacob Faibussowitsch inline bool MemoryBlock<T, A, S>::owns_pointer(const T *ptr) const noexcept
414d71ae5a4SJacob Faibussowitsch {
4150e6b6b59SJacob Faibussowitsch   // each pool is linear in memory, so it suffices to check the bounds
4160e6b6b59SJacob Faibussowitsch   return (ptr >= mem_) && (ptr < std::next(mem_, size()));
4170e6b6b59SJacob Faibussowitsch }
4180e6b6b59SJacob Faibussowitsch 
4190e6b6b59SJacob Faibussowitsch /*
4200e6b6b59SJacob Faibussowitsch   MemoryBlock::try_allocate_chunk - try to get a chunk from this MemoryBlock
4210e6b6b59SJacob Faibussowitsch 
4220e6b6b59SJacob Faibussowitsch   Input Parameters:
4230e6b6b59SJacob Faibussowitsch + req_size - the requested size of the allocation (in elements)
4240e6b6b59SJacob Faibussowitsch . ptr      - ptr to fill
4250e6b6b59SJacob Faibussowitsch - stream   - stream to fill the pointer on
4260e6b6b59SJacob Faibussowitsch 
4270e6b6b59SJacob Faibussowitsch   Output Parameter:
4280e6b6b59SJacob Faibussowitsch . success  - true if chunk was gotten, false otherwise
4290e6b6b59SJacob Faibussowitsch 
4300e6b6b59SJacob Faibussowitsch   Notes:
4310e6b6b59SJacob Faibussowitsch   If the current memory could not satisfy the memory request, ptr is unchanged
4320e6b6b59SJacob Faibussowitsch */
4330e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
434d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_allocate_chunk(size_type req_size, T **ptr, const stream_type *stream, bool *success) noexcept
435d71ae5a4SJacob Faibussowitsch {
4360e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4370e6b6b59SJacob Faibussowitsch   *success = false;
4380e6b6b59SJacob Faibussowitsch   if (req_size <= size()) {
4390e6b6b59SJacob Faibussowitsch     const auto try_create_chunk = [&]() {
4400e6b6b59SJacob Faibussowitsch       const auto was_empty     = chunks_.empty();
4410e6b6b59SJacob Faibussowitsch       const auto block_alloced = was_empty ? 0 : chunks_.back().total_offset();
4420e6b6b59SJacob Faibussowitsch 
4430e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4440e6b6b59SJacob Faibussowitsch       if (block_alloced + req_size <= size()) {
4450e6b6b59SJacob Faibussowitsch         PetscCallCXX(chunks_.emplace_back(block_alloced, req_size));
4460e6b6b59SJacob Faibussowitsch         PetscCall(chunks_.back().claim(stream, req_size, success));
4470e6b6b59SJacob Faibussowitsch         *ptr = mem_ + block_alloced;
4480e6b6b59SJacob 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());
4490e6b6b59SJacob Faibussowitsch       }
4500e6b6b59SJacob Faibussowitsch       PetscFunctionReturn(0);
4510e6b6b59SJacob Faibussowitsch     };
4520e6b6b59SJacob Faibussowitsch     const auto try_find_open_chunk = [&](bool serialize = false) {
4530e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4540e6b6b59SJacob Faibussowitsch       for (auto &chunk : chunks_) {
4550e6b6b59SJacob Faibussowitsch         PetscCall(chunk.claim(stream, req_size, success, serialize));
4560e6b6b59SJacob Faibussowitsch         if (*success) {
4570e6b6b59SJacob Faibussowitsch           *ptr = mem_ + chunk.start();
4580e6b6b59SJacob Faibussowitsch           break;
4590e6b6b59SJacob Faibussowitsch         }
4600e6b6b59SJacob Faibussowitsch       }
4610e6b6b59SJacob Faibussowitsch       PetscFunctionReturn(0);
4620e6b6b59SJacob Faibussowitsch     };
4636797ed33SJacob Faibussowitsch     const auto try_steal_other_stream_chunk = [&]() {
4646797ed33SJacob Faibussowitsch       PetscFunctionBegin;
4656797ed33SJacob Faibussowitsch       PetscCall(try_find_open_chunk(true));
4666797ed33SJacob Faibussowitsch       PetscFunctionReturn(0);
4676797ed33SJacob Faibussowitsch     };
4680e6b6b59SJacob Faibussowitsch 
4690e6b6b59SJacob Faibussowitsch     // search previously distributed chunks, but only claim one if it is on the same stream
4700e6b6b59SJacob Faibussowitsch     // as us
4710e6b6b59SJacob Faibussowitsch     PetscCall(try_find_open_chunk());
4720e6b6b59SJacob Faibussowitsch 
4730e6b6b59SJacob Faibussowitsch     // if we are here we couldn't reuse one of our own chunks so check first if the pool
4740e6b6b59SJacob Faibussowitsch     // has room for a new one
4750e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4760e6b6b59SJacob Faibussowitsch 
4770e6b6b59SJacob Faibussowitsch     // try pruning dead chunks off the back, note we do this regardless of whether we are
4780e6b6b59SJacob Faibussowitsch     // successful
4790e6b6b59SJacob Faibussowitsch     while (chunks_.back().can_claim(stream, 0, false)) {
4800e6b6b59SJacob Faibussowitsch       PetscCallCXX(chunks_.pop_back());
4810e6b6b59SJacob Faibussowitsch       if (chunks_.empty()) {
4820e6b6b59SJacob Faibussowitsch         // if chunks are empty it implies we have managed to claim (and subsequently destroy)
4830e6b6b59SJacob Faibussowitsch         // our own chunk twice! something has gone wrong
4840e6b6b59SJacob 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());
4850e6b6b59SJacob Faibussowitsch         break;
4860e6b6b59SJacob Faibussowitsch       }
4870e6b6b59SJacob Faibussowitsch     }
4880e6b6b59SJacob Faibussowitsch 
4890e6b6b59SJacob Faibussowitsch     // if previously unsuccessful see if enough space has opened up due to pruning. note that
4900e6b6b59SJacob Faibussowitsch     // if the chunk list was emptied from the pruning this call must succeed in allocating a
4910e6b6b59SJacob Faibussowitsch     // chunk, otherwise something is wrong
4920e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4930e6b6b59SJacob Faibussowitsch 
4940e6b6b59SJacob Faibussowitsch     // last resort, iterate over all chunks and see if we can steal one by waiting on the
4950e6b6b59SJacob Faibussowitsch     // current owner to finish using it
4966797ed33SJacob Faibussowitsch     if (!*success) PetscCall(try_steal_other_stream_chunk());
4970e6b6b59SJacob Faibussowitsch   }
4980e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4990e6b6b59SJacob Faibussowitsch }
5000e6b6b59SJacob Faibussowitsch 
5010e6b6b59SJacob Faibussowitsch /*
5020e6b6b59SJacob Faibussowitsch   MemoryBlock::try_deallocate_chunk - try to restore a chunk to this MemoryBlock
5030e6b6b59SJacob Faibussowitsch 
5040e6b6b59SJacob Faibussowitsch   Input Parameters:
5050e6b6b59SJacob Faibussowitsch + ptr     - ptr to restore
5060e6b6b59SJacob Faibussowitsch - stream  - stream to restore the pointer on
5070e6b6b59SJacob Faibussowitsch 
5080e6b6b59SJacob Faibussowitsch   Output Parameter:
5090e6b6b59SJacob Faibussowitsch . success - true if chunk was restored, false otherwise
5100e6b6b59SJacob Faibussowitsch 
5110e6b6b59SJacob Faibussowitsch   Notes:
5120e6b6b59SJacob Faibussowitsch   ptr is set to nullptr on successful restore, and is unchanged otherwise. If the ptr is owned
5130e6b6b59SJacob Faibussowitsch   by this MemoryBlock then it is restored on stream. The same stream may recieve ptr again
5140e6b6b59SJacob Faibussowitsch   without synchronization, but other streams may not do so until either serializing or the
5150e6b6b59SJacob Faibussowitsch   stream is idle again.
5160e6b6b59SJacob Faibussowitsch */
5170e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
518d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_deallocate_chunk(T **ptr, const stream_type *stream, bool *success) noexcept
519d71ae5a4SJacob Faibussowitsch {
5200e6b6b59SJacob Faibussowitsch   chunk_type *chunk = nullptr;
5210e6b6b59SJacob Faibussowitsch 
5220e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5230e6b6b59SJacob Faibussowitsch   PetscCall(try_find_chunk(*ptr, &chunk));
5240e6b6b59SJacob Faibussowitsch   if (chunk) {
5250e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
5260e6b6b59SJacob Faibussowitsch     *ptr     = nullptr;
5270e6b6b59SJacob Faibussowitsch     *success = true;
5280e6b6b59SJacob Faibussowitsch   } else {
5290e6b6b59SJacob Faibussowitsch     *success = false;
5300e6b6b59SJacob Faibussowitsch   }
5310e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5320e6b6b59SJacob Faibussowitsch }
5330e6b6b59SJacob Faibussowitsch 
5340e6b6b59SJacob Faibussowitsch /*
5350e6b6b59SJacob Faibussowitsch   MemoryBlock::try_find_chunk - try to find the chunk which owns ptr
5360e6b6b59SJacob Faibussowitsch 
5370e6b6b59SJacob Faibussowitsch   Input Parameter:
5380e6b6b59SJacob Faibussowitsch . ptr - the pointer to lookk for
5390e6b6b59SJacob Faibussowitsch 
5400e6b6b59SJacob Faibussowitsch   Output Parameter:
5410e6b6b59SJacob Faibussowitsch . ret_chunk - pointer to the owning chunk or nullptr if not found
5420e6b6b59SJacob Faibussowitsch */
5430e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
544d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_find_chunk(const T *ptr, chunk_type **ret_chunk) noexcept
545d71ae5a4SJacob Faibussowitsch {
5460e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5470e6b6b59SJacob Faibussowitsch   *ret_chunk = nullptr;
5480e6b6b59SJacob Faibussowitsch   if (owns_pointer(ptr)) {
5490e6b6b59SJacob Faibussowitsch     const auto offset = static_cast<size_type>(ptr - mem_);
5500e6b6b59SJacob Faibussowitsch 
5510e6b6b59SJacob Faibussowitsch     for (auto &chunk : chunks_) {
5526797ed33SJacob Faibussowitsch       if (chunk.contains(offset)) {
5530e6b6b59SJacob Faibussowitsch         *ret_chunk = &chunk;
5540e6b6b59SJacob Faibussowitsch         break;
5550e6b6b59SJacob Faibussowitsch       }
5560e6b6b59SJacob Faibussowitsch     }
5570e6b6b59SJacob Faibussowitsch 
5580e6b6b59SJacob 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())));
5590e6b6b59SJacob Faibussowitsch   }
5600e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
5610e6b6b59SJacob Faibussowitsch }
5620e6b6b59SJacob Faibussowitsch 
563d71ae5a4SJacob Faibussowitsch namespace detail
564d71ae5a4SJacob Faibussowitsch {
5650e6b6b59SJacob Faibussowitsch 
5660e6b6b59SJacob Faibussowitsch template <typename T>
5670e6b6b59SJacob Faibussowitsch struct real_type {
5680e6b6b59SJacob Faibussowitsch   using type = T;
5690e6b6b59SJacob Faibussowitsch };
5700e6b6b59SJacob Faibussowitsch 
5710e6b6b59SJacob Faibussowitsch template <>
5720e6b6b59SJacob Faibussowitsch struct real_type<PetscScalar> {
5730e6b6b59SJacob Faibussowitsch   using type = PetscReal;
5740e6b6b59SJacob Faibussowitsch };
5750e6b6b59SJacob Faibussowitsch 
5760e6b6b59SJacob Faibussowitsch } // namespace detail
5770e6b6b59SJacob Faibussowitsch 
5780e6b6b59SJacob Faibussowitsch template <typename T>
5790e6b6b59SJacob Faibussowitsch struct SegmentedMemoryPoolAllocatorBase {
5800e6b6b59SJacob Faibussowitsch   using value_type      = T;
5810e6b6b59SJacob Faibussowitsch   using size_type       = std::size_t;
5820e6b6b59SJacob Faibussowitsch   using real_value_type = typename detail::real_type<T>::type;
5830e6b6b59SJacob Faibussowitsch 
5840e6b6b59SJacob Faibussowitsch   template <typename U>
5850e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode allocate(value_type **, size_type, const device::StreamBase<U> *) noexcept;
5860e6b6b59SJacob Faibussowitsch   template <typename U>
5870e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode deallocate(value_type *, const device::StreamBase<U> *) noexcept;
5880e6b6b59SJacob Faibussowitsch   template <typename U>
5890e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode zero(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5900e6b6b59SJacob Faibussowitsch   template <typename U>
5910e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode uninitialized_copy(value_type *, const value_type *, size_type, const device::StreamBase<U> *) noexcept;
5920e6b6b59SJacob Faibussowitsch   template <typename U>
5930e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode set_canary(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5940e6b6b59SJacob Faibussowitsch };
5950e6b6b59SJacob Faibussowitsch 
5960e6b6b59SJacob Faibussowitsch template <typename T>
5970e6b6b59SJacob Faibussowitsch template <typename U>
598d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::allocate(value_type **ptr, size_type n, const device::StreamBase<U> *) noexcept
599d71ae5a4SJacob Faibussowitsch {
6000e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6010e6b6b59SJacob Faibussowitsch   PetscCall(PetscMalloc1(n, ptr));
6020e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6030e6b6b59SJacob Faibussowitsch }
6040e6b6b59SJacob Faibussowitsch 
6050e6b6b59SJacob Faibussowitsch template <typename T>
6060e6b6b59SJacob Faibussowitsch template <typename U>
607d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::deallocate(value_type *ptr, const device::StreamBase<U> *) noexcept
608d71ae5a4SJacob Faibussowitsch {
6090e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6100e6b6b59SJacob Faibussowitsch   PetscCall(PetscFree(ptr));
6110e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6120e6b6b59SJacob Faibussowitsch }
6130e6b6b59SJacob Faibussowitsch 
6140e6b6b59SJacob Faibussowitsch template <typename T>
6150e6b6b59SJacob Faibussowitsch template <typename U>
616d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::zero(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept
617d71ae5a4SJacob Faibussowitsch {
6180e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6190e6b6b59SJacob Faibussowitsch   PetscCall(PetscArrayzero(ptr, n));
6200e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6210e6b6b59SJacob Faibussowitsch }
6220e6b6b59SJacob Faibussowitsch 
6230e6b6b59SJacob Faibussowitsch template <typename T>
6240e6b6b59SJacob Faibussowitsch template <typename U>
625d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::uninitialized_copy(value_type *dest, const value_type *src, size_type n, const device::StreamBase<U> *) noexcept
626d71ae5a4SJacob Faibussowitsch {
6270e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6280e6b6b59SJacob Faibussowitsch   PetscCall(PetscArraycpy(dest, src, n));
6290e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6300e6b6b59SJacob Faibussowitsch }
6310e6b6b59SJacob Faibussowitsch 
6320e6b6b59SJacob Faibussowitsch template <typename T>
6330e6b6b59SJacob Faibussowitsch template <typename U>
634d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::set_canary(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept
635d71ae5a4SJacob Faibussowitsch {
6360e6b6b59SJacob Faibussowitsch   using limit_type            = std::numeric_limits<real_value_type>;
6370e6b6b59SJacob Faibussowitsch   constexpr value_type canary = limit_type::has_signaling_NaN ? limit_type::signaling_NaN() : limit_type::max();
6380e6b6b59SJacob Faibussowitsch 
6390e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6400e6b6b59SJacob Faibussowitsch   for (size_type i = 0; i < n; ++i) ptr[i] = canary;
6410e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6420e6b6b59SJacob Faibussowitsch }
6430e6b6b59SJacob Faibussowitsch 
6440e6b6b59SJacob Faibussowitsch } // namespace impl
6450e6b6b59SJacob Faibussowitsch 
6460e6b6b59SJacob Faibussowitsch // ==========================================================================================
6470e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool
6486797ed33SJacob Faibussowitsch //
6496797ed33SJacob Faibussowitsch // Stream-aware async memory allocator. Holds a list of memory "blocks" which each control an
6506797ed33SJacob Faibussowitsch // allocated buffer. This buffer is further split into memory "chunks" which control
6516797ed33SJacob Faibussowitsch // consecutive, non-overlapping regions of the block. Chunks may be in 1 of 2 states:
6526797ed33SJacob Faibussowitsch //
6536797ed33SJacob Faibussowitsch // 1. Open:
6546797ed33SJacob Faibussowitsch //    The chunk is free to be claimed by the next suitable allocation request. If the
6556797ed33SJacob Faibussowitsch //    allocation request is made on the same stream as the chunk was deallocated on, no
6566797ed33SJacob Faibussowitsch //    serialization needs to occur. If not, the allocating stream must wait for the
6576797ed33SJacob Faibussowitsch //    event. Claiming the chunk "closes" the chunk.
6586797ed33SJacob Faibussowitsch //
6596797ed33SJacob Faibussowitsch // 2. Closed:
6606797ed33SJacob Faibussowitsch //    The chunk has been claimed by an allocation request. It cannot be opened again until it
6616797ed33SJacob Faibussowitsch //    is deallocated; doing so "opens" the chunk.
6626797ed33SJacob Faibussowitsch //
6636797ed33SJacob Faibussowitsch // Note that there does not need to be a chunk for every region, chunks are created to satisfy
6646797ed33SJacob Faibussowitsch // an allocation request.
6656797ed33SJacob Faibussowitsch //
6666797ed33SJacob Faibussowitsch // Thus there is usually a region of "unallocated" memory at the end of the buffer, which may
6676797ed33SJacob Faibussowitsch // be claimed by a newly created chunk if existing chunks cannot satisfy the allocation
6686797ed33SJacob Faibussowitsch // request. This region exists _only_ at the end, as there are no gaps between chunks.
6696797ed33SJacob Faibussowitsch //
6706797ed33SJacob Faibussowitsch //
6716797ed33SJacob Faibussowitsch // |-----------------------------------------------------------------------------------------
6726797ed33SJacob Faibussowitsch // | SegmentedMemoryPool
6736797ed33SJacob Faibussowitsch // |
6746797ed33SJacob Faibussowitsch // | ||-------------||
6756797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
6766797ed33SJacob Faibussowitsch // | ||             ||    | AAAAAAAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDXXXXXXXX...
6776797ed33SJacob Faibussowitsch // | ||             ||    | |             |      |                   |            |
6786797ed33SJacob Faibussowitsch // | ||             ||    | x-----x-------x-----xx---------x---------x------x-----x
6796797ed33SJacob Faibussowitsch // | || MemoryBlock || -> | ------|-------------|----------|----------------|--------
6806797ed33SJacob Faibussowitsch // | ||             ||    | | MemoryChunk | MemoryChunk | MemoryChunk | MemoryChunk |
6816797ed33SJacob Faibussowitsch // | ||             ||    | ---------------------------------------------------------
6826797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
6836797ed33SJacob Faibussowitsch // | ||-------------||
6846797ed33SJacob Faibussowitsch // | ||             ||
6856797ed33SJacob Faibussowitsch // | ||     ...     ||
6866797ed33SJacob Faibussowitsch // | ||             ||
6870e6b6b59SJacob Faibussowitsch // ==========================================================================================
6880e6b6b59SJacob Faibussowitsch 
6890e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType = device::DefaultStream, typename AllocType = impl::SegmentedMemoryPoolAllocatorBase<MemType>, std::size_t DefaultChunkSize = 256>
6900e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool;
6910e6b6b59SJacob Faibussowitsch 
6920e6b6b59SJacob Faibussowitsch // The actual memory pool class. It is in essence just a wrapper for a list of MemoryBlocks.
6930e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
6940e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool : public RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>> {
6950e6b6b59SJacob Faibussowitsch public:
6960e6b6b59SJacob Faibussowitsch   using value_type     = MemType;
6970e6b6b59SJacob Faibussowitsch   using stream_type    = StreamType;
6980e6b6b59SJacob Faibussowitsch   using allocator_type = AllocType;
6990e6b6b59SJacob Faibussowitsch   using block_type     = impl::MemoryBlock<value_type, allocator_type, stream_type>;
7000e6b6b59SJacob Faibussowitsch   using pool_type      = std::deque<block_type>;
7010e6b6b59SJacob Faibussowitsch   using size_type      = typename block_type::size_type;
7020e6b6b59SJacob Faibussowitsch 
7030e6b6b59SJacob Faibussowitsch   explicit SegmentedMemoryPool(AllocType = AllocType{}, std::size_t = DefaultChunkSize) noexcept(std::is_nothrow_default_constructible<pool_type>::value);
7040e6b6b59SJacob Faibussowitsch 
7056797ed33SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode allocate(PetscInt, value_type **, const stream_type *, size_type = std::alignment_of<MemType>::value) noexcept;
7060e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode deallocate(value_type **, const stream_type *) noexcept;
7070e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode reallocate(PetscInt, value_type **, const stream_type *) noexcept;
7080e6b6b59SJacob Faibussowitsch 
7090e6b6b59SJacob Faibussowitsch private:
7100e6b6b59SJacob Faibussowitsch   pool_type      pool_;
7110e6b6b59SJacob Faibussowitsch   allocator_type allocator_;
7120e6b6b59SJacob Faibussowitsch   size_type      chunk_size_;
7130e6b6b59SJacob Faibussowitsch 
7140e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode make_block_(size_type, const stream_type *) noexcept;
7150e6b6b59SJacob Faibussowitsch 
7160e6b6b59SJacob Faibussowitsch   friend class RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>>;
7170e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode register_finalize_(const stream_type *) noexcept;
7180e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode finalize_() noexcept;
7196797ed33SJacob Faibussowitsch 
7206797ed33SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode allocate_(size_type, value_type **, const stream_type *) noexcept;
7210e6b6b59SJacob Faibussowitsch };
7220e6b6b59SJacob Faibussowitsch 
7230e6b6b59SJacob Faibussowitsch // ==========================================================================================
7240e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Private API
7250e6b6b59SJacob Faibussowitsch // ==========================================================================================
7260e6b6b59SJacob Faibussowitsch 
7270e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
728d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::make_block_(size_type size, const stream_type *stream) noexcept
729d71ae5a4SJacob Faibussowitsch {
7300e6b6b59SJacob Faibussowitsch   const auto block_size = std::max(size, chunk_size_);
7310e6b6b59SJacob Faibussowitsch 
7320e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7330e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.emplace_back(&allocator_, block_size, stream));
7340e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Allocated new block of size %zu, total %zu blocks\n", block_size, pool_.size()));
7350e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7360e6b6b59SJacob Faibussowitsch }
7370e6b6b59SJacob Faibussowitsch 
7380e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
739d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::register_finalize_(const stream_type *stream) noexcept
740d71ae5a4SJacob Faibussowitsch {
7410e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7420e6b6b59SJacob Faibussowitsch   PetscCall(make_block_(chunk_size_, stream));
7430e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7440e6b6b59SJacob Faibussowitsch }
7450e6b6b59SJacob Faibussowitsch 
7460e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
747d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::finalize_() noexcept
748d71ae5a4SJacob Faibussowitsch {
7490e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7500e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.clear());
7510e6b6b59SJacob Faibussowitsch   chunk_size_ = DefaultChunkSize;
7520e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
7530e6b6b59SJacob Faibussowitsch }
7540e6b6b59SJacob Faibussowitsch 
7556797ed33SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
756d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate_(size_type size, value_type **ptr, const stream_type *stream) noexcept
757d71ae5a4SJacob Faibussowitsch {
7586797ed33SJacob Faibussowitsch   auto found = false;
7596797ed33SJacob Faibussowitsch 
7606797ed33SJacob Faibussowitsch   PetscFunctionBegin;
761*503aa7efSJacob Faibussowitsch   PetscCall(this->register_finalize(stream));
7626797ed33SJacob Faibussowitsch   for (auto &block : pool_) {
7636797ed33SJacob Faibussowitsch     PetscCall(block.try_allocate_chunk(size, ptr, stream, &found));
7646797ed33SJacob Faibussowitsch     if (PetscLikely(found)) PetscFunctionReturn(0);
7656797ed33SJacob Faibussowitsch   }
7666797ed33SJacob Faibussowitsch 
7676797ed33SJacob 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));
7686797ed33SJacob Faibussowitsch   // if we are here we couldn't find an open block in the pool, so make a new block
7696797ed33SJacob Faibussowitsch   PetscCall(make_block_(size, stream));
7706797ed33SJacob Faibussowitsch   // and assign it
7716797ed33SJacob Faibussowitsch   PetscCall(pool_.back().try_allocate_chunk(size, ptr, stream, &found));
7726797ed33SJacob 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());
7736797ed33SJacob Faibussowitsch   PetscFunctionReturn(0);
7746797ed33SJacob Faibussowitsch }
7756797ed33SJacob Faibussowitsch 
7760e6b6b59SJacob Faibussowitsch // ==========================================================================================
7770e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Public API
7780e6b6b59SJacob Faibussowitsch // ==========================================================================================
7790e6b6b59SJacob Faibussowitsch 
7800e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
781d71ae5a4SJacob 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)
782d71ae5a4SJacob Faibussowitsch {
783d71ae5a4SJacob Faibussowitsch }
7840e6b6b59SJacob Faibussowitsch 
7850e6b6b59SJacob Faibussowitsch /*
7860e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::allocate - get an allocation from the memory pool
7870e6b6b59SJacob Faibussowitsch 
7880e6b6b59SJacob Faibussowitsch   Input Parameters:
7890e6b6b59SJacob Faibussowitsch + req_size - size (in elements) to get
7900e6b6b59SJacob Faibussowitsch . ptr      - the pointer to hold the allocation
7910e6b6b59SJacob Faibussowitsch - stream   - the stream on which to get the allocation
7920e6b6b59SJacob Faibussowitsch 
7930e6b6b59SJacob Faibussowitsch   Output Parameter:
7940e6b6b59SJacob Faibussowitsch . ptr - the pointer holding the allocation
7950e6b6b59SJacob Faibussowitsch 
7960e6b6b59SJacob Faibussowitsch   Notes:
7970e6b6b59SJacob Faibussowitsch   req_size cannot be negative. If req_size if zero, ptr is set to nullptr
7980e6b6b59SJacob Faibussowitsch */
7990e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
800d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate(PetscInt req_size, value_type **ptr, const stream_type *stream, size_type alignment) noexcept
801d71ae5a4SJacob Faibussowitsch {
8026797ed33SJacob Faibussowitsch   value_type *ret_ptr = nullptr;
8030e6b6b59SJacob Faibussowitsch 
8040e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8050e6b6b59SJacob Faibussowitsch   PetscAssert(req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", req_size);
8060e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 2);
8070e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 3);
8086797ed33SJacob Faibussowitsch   if (req_size) {
8096797ed33SJacob Faibussowitsch     const auto size         = static_cast<size_type>(req_size);
8106797ed33SJacob Faibussowitsch     auto       aligned_size = alignment == alignof(char) ? size : size + alignment;
8116797ed33SJacob Faibussowitsch     void      *vptr         = nullptr;
8120e6b6b59SJacob Faibussowitsch 
8136797ed33SJacob Faibussowitsch     PetscCall(allocate_(aligned_size, &ret_ptr, stream));
8146797ed33SJacob Faibussowitsch     vptr = ret_ptr;
8156797ed33SJacob Faibussowitsch     std::align(alignment, size, vptr, aligned_size);
8166797ed33SJacob Faibussowitsch     ret_ptr = reinterpret_cast<value_type *>(vptr);
8176797ed33SJacob Faibussowitsch     // sets memory to NaN or infinity depending on the type to catch out uninitialized memory
8186797ed33SJacob Faibussowitsch     // accesses.
8196797ed33SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) PetscCall(allocator_.set_canary(ret_ptr, size, stream));
8206797ed33SJacob Faibussowitsch   }
8216797ed33SJacob Faibussowitsch   *ptr = ret_ptr;
8220e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
8230e6b6b59SJacob Faibussowitsch }
8240e6b6b59SJacob Faibussowitsch 
8250e6b6b59SJacob Faibussowitsch /*
8260e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::deallocate - release a pointer back to the memory pool
8270e6b6b59SJacob Faibussowitsch 
8280e6b6b59SJacob Faibussowitsch   Input Parameters:
8290e6b6b59SJacob Faibussowitsch + ptr    - the pointer to release
8300e6b6b59SJacob Faibussowitsch - stream - the stream to release it on
8310e6b6b59SJacob Faibussowitsch 
8320e6b6b59SJacob Faibussowitsch   Notes:
8330e6b6b59SJacob Faibussowitsch   If ptr is not owned by the pool it is unchanged.
8340e6b6b59SJacob Faibussowitsch */
8350e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
836d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::deallocate(value_type **ptr, const stream_type *stream) noexcept
837d71ae5a4SJacob Faibussowitsch {
8380e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8390e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 1);
8400e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 2);
8410e6b6b59SJacob Faibussowitsch   // nobody owns a nullptr, and if they do then they have bigger problems
8420e6b6b59SJacob Faibussowitsch   if (!*ptr) PetscFunctionReturn(0);
8430e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8440e6b6b59SJacob Faibussowitsch     auto found = false;
8450e6b6b59SJacob Faibussowitsch 
8460e6b6b59SJacob Faibussowitsch     PetscCall(block.try_deallocate_chunk(ptr, stream, &found));
8470e6b6b59SJacob Faibussowitsch     if (PetscLikely(found)) break;
8480e6b6b59SJacob Faibussowitsch   }
8490e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
8500e6b6b59SJacob Faibussowitsch }
8510e6b6b59SJacob Faibussowitsch 
8520e6b6b59SJacob Faibussowitsch /*
8530e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::reallocate - Resize an allocated buffer
8540e6b6b59SJacob Faibussowitsch 
8550e6b6b59SJacob Faibussowitsch   Input Parameters:
8560e6b6b59SJacob Faibussowitsch + new_req_size - the new buffer size
8570e6b6b59SJacob Faibussowitsch . ptr          - pointer to the buffer
8580e6b6b59SJacob Faibussowitsch - stream       - stream to resize with
8590e6b6b59SJacob Faibussowitsch 
860d5b43468SJose E. Roman   Output Parameter:
8610e6b6b59SJacob Faibussowitsch . ptr - pointer to the new region
8620e6b6b59SJacob Faibussowitsch 
8630e6b6b59SJacob Faibussowitsch   Notes:
8640e6b6b59SJacob Faibussowitsch   ptr must have been allocated by the pool.
8650e6b6b59SJacob Faibussowitsch 
8660e6b6b59SJacob Faibussowitsch   It's OK to shrink the buffer, even down to 0 (in which case it is just deallocated).
8670e6b6b59SJacob Faibussowitsch */
8680e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
869d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::reallocate(PetscInt new_req_size, value_type **ptr, const stream_type *stream) noexcept
870d71ae5a4SJacob Faibussowitsch {
8710e6b6b59SJacob Faibussowitsch   using chunk_type = typename block_type::chunk_type;
8720e6b6b59SJacob Faibussowitsch 
8730e6b6b59SJacob Faibussowitsch   const auto  new_size = static_cast<size_type>(new_req_size);
8740e6b6b59SJacob Faibussowitsch   const auto  old_ptr  = *ptr;
8750e6b6b59SJacob Faibussowitsch   chunk_type *chunk    = nullptr;
8760e6b6b59SJacob Faibussowitsch 
8770e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8780e6b6b59SJacob Faibussowitsch   PetscAssert(new_req_size >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Requested memory amount (%" PetscInt_FMT ") must be >= 0", new_req_size);
8790e6b6b59SJacob Faibussowitsch   PetscValidPointer(ptr, 2);
8800e6b6b59SJacob Faibussowitsch   PetscValidPointer(stream, 3);
8810e6b6b59SJacob Faibussowitsch 
8820e6b6b59SJacob Faibussowitsch   // if reallocating to zero, just free
8830e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(new_size == 0)) {
8840e6b6b59SJacob Faibussowitsch     PetscCall(deallocate(ptr, stream));
8850e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
8860e6b6b59SJacob Faibussowitsch   }
8870e6b6b59SJacob Faibussowitsch 
8880e6b6b59SJacob Faibussowitsch   // search the blocks for the owning chunk
8890e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8900e6b6b59SJacob Faibussowitsch     PetscCall(block.try_find_chunk(old_ptr, &chunk));
8910e6b6b59SJacob Faibussowitsch     if (chunk) break; // found
8920e6b6b59SJacob Faibussowitsch   }
8930e6b6b59SJacob Faibussowitsch   PetscAssert(chunk, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Memory pool does not own %p, so cannot reallocate it", *ptr);
8940e6b6b59SJacob Faibussowitsch 
8950e6b6b59SJacob Faibussowitsch   if (chunk->capacity() < new_size) {
8960e6b6b59SJacob Faibussowitsch     // chunk does not have enough room, need to grab a fresh chunk and copy to it
8970e6b6b59SJacob Faibussowitsch     *ptr = nullptr;
8980e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
8990e6b6b59SJacob Faibussowitsch     PetscCall(allocate(new_size, ptr, stream));
9000e6b6b59SJacob Faibussowitsch     PetscCall(allocator_.uninitialized_copy(*ptr, old_ptr, new_size, stream));
9010e6b6b59SJacob Faibussowitsch   } else {
9020e6b6b59SJacob Faibussowitsch     // chunk had enough room we can simply grow (or shrink) to fit the new size
9030e6b6b59SJacob Faibussowitsch     PetscCall(chunk->resize(new_size));
9040e6b6b59SJacob Faibussowitsch   }
9050e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
9060e6b6b59SJacob Faibussowitsch }
9070e6b6b59SJacob Faibussowitsch 
9080e6b6b59SJacob Faibussowitsch } // namespace memory
9090e6b6b59SJacob Faibussowitsch 
9100e6b6b59SJacob Faibussowitsch } // namespace Petsc
9110e6b6b59SJacob Faibussowitsch 
9120e6b6b59SJacob Faibussowitsch #endif // PETSC_SEGMENTEDMEMPOOL_HPP
913