xref: /petsc/src/sys/objects/device/impls/segmentedmempool.hpp (revision 7f031e8bf1f008cdd443cdad0cd45837cb20997c)
1a4963045SJacob Faibussowitsch #pragma once
20e6b6b59SJacob Faibussowitsch 
30e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h>
46797ed33SJacob Faibussowitsch 
50e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/macros.hpp>
60e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp>
70e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/utility.hpp>
80e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp>
96797ed33SJacob Faibussowitsch #include <petsc/private/cpp/memory.hpp>
100e6b6b59SJacob Faibussowitsch 
110e6b6b59SJacob Faibussowitsch #include <limits>
120e6b6b59SJacob Faibussowitsch #include <deque>
130e6b6b59SJacob Faibussowitsch #include <vector>
140e6b6b59SJacob Faibussowitsch 
15d71ae5a4SJacob Faibussowitsch namespace Petsc
16d71ae5a4SJacob Faibussowitsch {
170e6b6b59SJacob Faibussowitsch 
18d71ae5a4SJacob Faibussowitsch namespace device
19d71ae5a4SJacob Faibussowitsch {
200e6b6b59SJacob Faibussowitsch 
210e6b6b59SJacob Faibussowitsch template <typename T>
220e6b6b59SJacob Faibussowitsch class StreamBase {
230e6b6b59SJacob Faibussowitsch public:
240e6b6b59SJacob Faibussowitsch   using id_type      = int;
250e6b6b59SJacob Faibussowitsch   using derived_type = T;
260e6b6b59SJacob Faibussowitsch 
270e6b6b59SJacob Faibussowitsch   static const id_type INVALID_ID;
280e6b6b59SJacob Faibussowitsch 
290e6b6b59SJacob Faibussowitsch   // needed so that dependent auto works, see veccupmimpl.h for a detailed discussion
300e6b6b59SJacob Faibussowitsch   template <typename U = T>
310e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD auto get_stream() const noexcept PETSC_DECLTYPE_AUTO_RETURNS(static_cast<const U &>(*this).get_stream_());
320e6b6b59SJacob Faibussowitsch 
get_id() const330e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD id_type get_id() const noexcept { return static_cast<const T &>(*this).get_id_(); }
340e6b6b59SJacob Faibussowitsch 
350e6b6b59SJacob Faibussowitsch   template <typename E>
record_event(E && event) const36089fb57cSJacob Faibussowitsch   PetscErrorCode record_event(E &&event) const noexcept
37d71ae5a4SJacob Faibussowitsch   {
380e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).record_event_(std::forward<E>(event));
390e6b6b59SJacob Faibussowitsch   }
400e6b6b59SJacob Faibussowitsch 
410e6b6b59SJacob Faibussowitsch   template <typename E>
wait_for_event(E && event) const42089fb57cSJacob Faibussowitsch   PetscErrorCode wait_for_event(E &&event) const noexcept
43d71ae5a4SJacob Faibussowitsch   {
440e6b6b59SJacob Faibussowitsch     return static_cast<const T &>(*this).wait_for_(std::forward<E>(event));
450e6b6b59SJacob Faibussowitsch   }
460e6b6b59SJacob Faibussowitsch 
470e6b6b59SJacob Faibussowitsch protected:
480e6b6b59SJacob Faibussowitsch   constexpr StreamBase() noexcept = default;
490e6b6b59SJacob Faibussowitsch 
500e6b6b59SJacob Faibussowitsch   struct default_event_type { };
510e6b6b59SJacob Faibussowitsch   using default_stream_type = std::nullptr_t;
520e6b6b59SJacob Faibussowitsch 
get_stream_()530e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr default_stream_type get_stream_() noexcept { return nullptr; }
540e6b6b59SJacob Faibussowitsch 
get_id_()550e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD static constexpr id_type get_id_() noexcept { return 0; }
560e6b6b59SJacob Faibussowitsch 
570e6b6b59SJacob Faibussowitsch   template <typename U = T>
record_event_(const typename U::event_type &)58089fb57cSJacob Faibussowitsch   static constexpr PetscErrorCode record_event_(const typename U::event_type &) noexcept
59d71ae5a4SJacob Faibussowitsch   {
603ba16761SJacob Faibussowitsch     return PETSC_SUCCESS;
610e6b6b59SJacob Faibussowitsch   }
620e6b6b59SJacob Faibussowitsch 
630e6b6b59SJacob Faibussowitsch   template <typename U = T>
wait_for_(const typename U::event_type &)64089fb57cSJacob Faibussowitsch   static constexpr PetscErrorCode wait_for_(const typename U::event_type &) noexcept
65d71ae5a4SJacob Faibussowitsch   {
663ba16761SJacob Faibussowitsch     return PETSC_SUCCESS;
670e6b6b59SJacob Faibussowitsch   }
680e6b6b59SJacob Faibussowitsch };
690e6b6b59SJacob Faibussowitsch 
700e6b6b59SJacob Faibussowitsch template <typename T>
710e6b6b59SJacob Faibussowitsch const typename StreamBase<T>::id_type StreamBase<T>::INVALID_ID = -1;
720e6b6b59SJacob Faibussowitsch 
730e6b6b59SJacob Faibussowitsch struct DefaultStream : StreamBase<DefaultStream> {
740e6b6b59SJacob Faibussowitsch   using stream_type = typename StreamBase<DefaultStream>::default_stream_type;
750e6b6b59SJacob Faibussowitsch   using id_type     = typename StreamBase<DefaultStream>::id_type;
760e6b6b59SJacob Faibussowitsch   using event_type  = typename StreamBase<DefaultStream>::default_event_type;
770e6b6b59SJacob Faibussowitsch };
780e6b6b59SJacob Faibussowitsch 
790e6b6b59SJacob Faibussowitsch } // namespace device
800e6b6b59SJacob Faibussowitsch 
81d71ae5a4SJacob Faibussowitsch namespace memory
82d71ae5a4SJacob Faibussowitsch {
830e6b6b59SJacob Faibussowitsch 
84d71ae5a4SJacob Faibussowitsch namespace impl
85d71ae5a4SJacob Faibussowitsch {
860e6b6b59SJacob Faibussowitsch 
870e6b6b59SJacob Faibussowitsch // ==========================================================================================
880e6b6b59SJacob Faibussowitsch // MemoryChunk
890e6b6b59SJacob Faibussowitsch //
900e6b6b59SJacob Faibussowitsch // Represents a checked-out region of a MemoryBlock. Tracks the offset into the owning
910e6b6b59SJacob Faibussowitsch // MemoryBlock and its size/capacity
920e6b6b59SJacob Faibussowitsch // ==========================================================================================
930e6b6b59SJacob Faibussowitsch 
940e6b6b59SJacob Faibussowitsch template <typename EventType>
950e6b6b59SJacob Faibussowitsch class MemoryChunk {
960e6b6b59SJacob Faibussowitsch public:
970e6b6b59SJacob Faibussowitsch   using event_type = EventType;
980e6b6b59SJacob Faibussowitsch   using size_type  = std::size_t;
990e6b6b59SJacob Faibussowitsch 
1000e6b6b59SJacob Faibussowitsch   MemoryChunk(size_type, size_type) noexcept;
1010e6b6b59SJacob Faibussowitsch   explicit MemoryChunk(size_type) noexcept;
1020e6b6b59SJacob Faibussowitsch 
1030e6b6b59SJacob Faibussowitsch   MemoryChunk(MemoryChunk &&) noexcept;
1040e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(MemoryChunk &&) noexcept;
1050e6b6b59SJacob Faibussowitsch 
1060e6b6b59SJacob Faibussowitsch   MemoryChunk(const MemoryChunk &) noexcept            = delete;
1070e6b6b59SJacob Faibussowitsch   MemoryChunk &operator=(const MemoryChunk &) noexcept = delete;
1080e6b6b59SJacob Faibussowitsch 
start() const1090e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type start() const noexcept { return start_; }
size() const1100e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
1110e6b6b59SJacob Faibussowitsch   // REVIEW ME:
1120e6b6b59SJacob Faibussowitsch   // make this an actual field, normally each chunk shrinks_to_fit() on begin claimed, but in
1130e6b6b59SJacob Faibussowitsch   // theory only the last chunk needs to do this
capacity() const1140e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type capacity() const noexcept { return size_; }
total_offset() const1150e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type total_offset() const noexcept { return start() + size(); }
1160e6b6b59SJacob Faibussowitsch 
1170e6b6b59SJacob Faibussowitsch   template <typename U>
118089fb57cSJacob Faibussowitsch   PetscErrorCode release(const device::StreamBase<U> *) noexcept;
1190e6b6b59SJacob Faibussowitsch   template <typename U>
120089fb57cSJacob Faibussowitsch   PetscErrorCode claim(const device::StreamBase<U> *, size_type, bool *, bool = false) noexcept;
1210e6b6b59SJacob Faibussowitsch   template <typename U>
1220e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool can_claim(const device::StreamBase<U> *, size_type, bool) const noexcept;
123089fb57cSJacob Faibussowitsch   PetscErrorCode       resize(size_type) noexcept;
1246797ed33SJacob Faibussowitsch   PETSC_NODISCARD bool contains(size_type) const noexcept;
1250e6b6b59SJacob Faibussowitsch 
1260e6b6b59SJacob Faibussowitsch private:
1270e6b6b59SJacob Faibussowitsch   event_type event_{};                                       // event recorded when the chunk was released
1280e6b6b59SJacob Faibussowitsch   bool       open_      = true;                              // is this chunk open?
129e2fbb1baSPierre Jolivet   int        stream_id_ = device::DefaultStream::INVALID_ID; // id of the last stream to use the chunk, populated on release
1300e6b6b59SJacob Faibussowitsch   size_type  size_      = 0;                                 // size of the chunk
131130d4ca2SJed Brown   size_type  start_     = 0;                                 // offset from the start of the owning block
1326797ed33SJacob Faibussowitsch 
1330e6b6b59SJacob Faibussowitsch   template <typename U>
1340e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool stream_compat_(const device::StreamBase<U> *) const noexcept;
1350e6b6b59SJacob Faibussowitsch };
1360e6b6b59SJacob Faibussowitsch 
1370e6b6b59SJacob Faibussowitsch // ==========================================================================================
1380e6b6b59SJacob Faibussowitsch // MemoryChunk - Private API
1390e6b6b59SJacob Faibussowitsch // ==========================================================================================
1400e6b6b59SJacob Faibussowitsch 
1410e6b6b59SJacob Faibussowitsch // asks and answers the question: can this stream claim this chunk without serializing?
1420e6b6b59SJacob Faibussowitsch template <typename E>
1430e6b6b59SJacob Faibussowitsch template <typename U>
stream_compat_(const device::StreamBase<U> * strm) const144d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::stream_compat_(const device::StreamBase<U> *strm) const noexcept
145d71ae5a4SJacob Faibussowitsch {
1460e6b6b59SJacob Faibussowitsch   return (stream_id_ == strm->INVALID_ID) || (stream_id_ == strm->get_id());
1470e6b6b59SJacob Faibussowitsch }
1480e6b6b59SJacob Faibussowitsch 
1490e6b6b59SJacob Faibussowitsch // ==========================================================================================
1500e6b6b59SJacob Faibussowitsch // MemoryChunk - Public API
1510e6b6b59SJacob Faibussowitsch // ==========================================================================================
1520e6b6b59SJacob Faibussowitsch 
1530e6b6b59SJacob Faibussowitsch template <typename E>
MemoryChunk(size_type start,size_type size)154d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type start, size_type size) noexcept : size_(size), start_(start)
155d71ae5a4SJacob Faibussowitsch {
156d71ae5a4SJacob Faibussowitsch }
1570e6b6b59SJacob Faibussowitsch 
1580e6b6b59SJacob Faibussowitsch template <typename E>
MemoryChunk(size_type size)159d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(size_type size) noexcept : MemoryChunk(0, size)
160d71ae5a4SJacob Faibussowitsch {
161d71ae5a4SJacob Faibussowitsch }
1620e6b6b59SJacob Faibussowitsch 
1630e6b6b59SJacob Faibussowitsch template <typename E>
MemoryChunk(MemoryChunk<E> && other)1640e6b6b59SJacob Faibussowitsch inline MemoryChunk<E>::MemoryChunk(MemoryChunk<E> &&other) noexcept :
165d71ae5a4SJacob 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_))
166d71ae5a4SJacob Faibussowitsch {
167d71ae5a4SJacob Faibussowitsch }
1680e6b6b59SJacob Faibussowitsch 
1690e6b6b59SJacob Faibussowitsch template <typename E>
operator =(MemoryChunk<E> && other)170d71ae5a4SJacob Faibussowitsch inline MemoryChunk<E> &MemoryChunk<E>::operator=(MemoryChunk<E> &&other) noexcept
171d71ae5a4SJacob Faibussowitsch {
1720e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1730e6b6b59SJacob Faibussowitsch   if (this != &other) {
1740e6b6b59SJacob Faibussowitsch     event_     = std::move(other.event_);
1750e6b6b59SJacob Faibussowitsch     open_      = util::exchange(other.open_, false);
1760e6b6b59SJacob Faibussowitsch     stream_id_ = util::exchange(other.stream_id_, device::DefaultStream::INVALID_ID);
1770e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
1780e6b6b59SJacob Faibussowitsch     start_     = std::move(other.start_);
1790e6b6b59SJacob Faibussowitsch   }
1800e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
1810e6b6b59SJacob Faibussowitsch }
1820e6b6b59SJacob Faibussowitsch 
1830e6b6b59SJacob Faibussowitsch /*
1840e6b6b59SJacob Faibussowitsch   MemoryChunk::release - release a chunk on a stream
1850e6b6b59SJacob Faibussowitsch 
1860e6b6b59SJacob Faibussowitsch   Input Parameter:
1870e6b6b59SJacob Faibussowitsch . stream - the stream to release the chunk with
1880e6b6b59SJacob Faibussowitsch 
1890e6b6b59SJacob Faibussowitsch   Notes:
1900e6b6b59SJacob Faibussowitsch   Inserts a release operation on stream and records the state of stream at the time this
1910e6b6b59SJacob Faibussowitsch   routine was called.
1920e6b6b59SJacob Faibussowitsch 
1930e6b6b59SJacob Faibussowitsch   Future allocation requests which attempt to claim the chunk on the same stream may re-acquire
1940e6b6b59SJacob Faibussowitsch   the chunk without serialization.
1950e6b6b59SJacob Faibussowitsch 
1960e6b6b59SJacob Faibussowitsch   If another stream attempts to claim the chunk they must wait for the recorded event before
1970e6b6b59SJacob Faibussowitsch   claiming the chunk.
1980e6b6b59SJacob Faibussowitsch */
1990e6b6b59SJacob Faibussowitsch template <typename E>
2000e6b6b59SJacob Faibussowitsch template <typename U>
release(const device::StreamBase<U> * stream)201d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::release(const device::StreamBase<U> *stream) noexcept
202d71ae5a4SJacob Faibussowitsch {
2030e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2040e6b6b59SJacob Faibussowitsch   open_      = true;
2050e6b6b59SJacob Faibussowitsch   stream_id_ = stream->get_id();
2060e6b6b59SJacob Faibussowitsch   PetscCall(stream->record_event(event_));
2073ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2080e6b6b59SJacob Faibussowitsch }
2090e6b6b59SJacob Faibussowitsch 
2100e6b6b59SJacob Faibussowitsch /*
2110e6b6b59SJacob Faibussowitsch   MemoryChunk::claim - attempt to claim a particular chunk
2120e6b6b59SJacob Faibussowitsch 
2130e6b6b59SJacob Faibussowitsch   Input Parameters:
2140e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2150e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2160e6b6b59SJacob Faibussowitsch - serialize - (optional, false) whether the claimant allows serialization
2170e6b6b59SJacob Faibussowitsch 
2180e6b6b59SJacob Faibussowitsch   Output Parameter:
2190e6b6b59SJacob Faibussowitsch . success - true if the chunk was claimed, false otherwise
2200e6b6b59SJacob Faibussowitsch */
2210e6b6b59SJacob Faibussowitsch template <typename E>
2220e6b6b59SJacob Faibussowitsch template <typename U>
claim(const device::StreamBase<U> * stream,size_type req_size,bool * success,bool serialize)223d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::claim(const device::StreamBase<U> *stream, size_type req_size, bool *success, bool serialize) noexcept
224d71ae5a4SJacob Faibussowitsch {
2250e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2260e6b6b59SJacob Faibussowitsch   if ((*success = can_claim(stream, req_size, serialize))) {
2270e6b6b59SJacob Faibussowitsch     if (serialize && !stream_compat_(stream)) PetscCall(stream->wait_for_event(event_));
2280e6b6b59SJacob Faibussowitsch     PetscCall(resize(req_size));
2290e6b6b59SJacob Faibussowitsch     open_ = false;
2300e6b6b59SJacob Faibussowitsch   }
2313ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2320e6b6b59SJacob Faibussowitsch }
2330e6b6b59SJacob Faibussowitsch 
2340e6b6b59SJacob Faibussowitsch /*
2350e6b6b59SJacob Faibussowitsch   MemoryChunk::can_claim - test whether a particular chunk can be claimed
2360e6b6b59SJacob Faibussowitsch 
2370e6b6b59SJacob Faibussowitsch   Input Parameters:
2380e6b6b59SJacob Faibussowitsch + stream    - the stream on which to attempt to claim
2390e6b6b59SJacob Faibussowitsch . req_size  - the requested size (in elements) to attempt to claim
2400e6b6b59SJacob Faibussowitsch - serialize - whether the claimant allows serialization
2410e6b6b59SJacob Faibussowitsch 
2420e6b6b59SJacob Faibussowitsch   Output:
2430e6b6b59SJacob Faibussowitsch . [return] - true if the chunk is claimable given the configuration, false otherwise
2440e6b6b59SJacob Faibussowitsch */
2450e6b6b59SJacob Faibussowitsch template <typename E>
2460e6b6b59SJacob Faibussowitsch template <typename U>
can_claim(const device::StreamBase<U> * stream,size_type req_size,bool serialize) const247d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::can_claim(const device::StreamBase<U> *stream, size_type req_size, bool serialize) const noexcept
248d71ae5a4SJacob Faibussowitsch {
2490e6b6b59SJacob Faibussowitsch   if (open_ && (req_size <= capacity())) {
2500e6b6b59SJacob Faibussowitsch     // fully compatible
2510e6b6b59SJacob Faibussowitsch     if (stream_compat_(stream)) return true;
2520e6b6b59SJacob Faibussowitsch     // stream wasn't compatible, but could claim if we serialized
2530e6b6b59SJacob Faibussowitsch     if (serialize) return true;
2540e6b6b59SJacob Faibussowitsch     // incompatible stream and did not want to serialize
2550e6b6b59SJacob Faibussowitsch   }
2560e6b6b59SJacob Faibussowitsch   return false;
2570e6b6b59SJacob Faibussowitsch }
2580e6b6b59SJacob Faibussowitsch 
2590e6b6b59SJacob Faibussowitsch /*
2600e6b6b59SJacob Faibussowitsch   MemoryChunk::resize - grow a chunk to new size
2610e6b6b59SJacob Faibussowitsch 
2620e6b6b59SJacob Faibussowitsch   Input Parameter:
2630e6b6b59SJacob Faibussowitsch . newsize - the new size Requested
2640e6b6b59SJacob Faibussowitsch 
2650e6b6b59SJacob Faibussowitsch   Notes:
2660e6b6b59SJacob Faibussowitsch   newsize cannot be larger than capacity
2670e6b6b59SJacob Faibussowitsch */
2680e6b6b59SJacob Faibussowitsch template <typename E>
resize(size_type newsize)269d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryChunk<E>::resize(size_type newsize) noexcept
270d71ae5a4SJacob Faibussowitsch {
2710e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2720e6b6b59SJacob Faibussowitsch   PetscAssert(newsize <= capacity(), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "New size %zu larger than capacity %zu", newsize, capacity());
2730e6b6b59SJacob Faibussowitsch   size_ = newsize;
2743ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2750e6b6b59SJacob Faibussowitsch }
2760e6b6b59SJacob Faibussowitsch 
2776797ed33SJacob Faibussowitsch /*
2786797ed33SJacob Faibussowitsch   MemoryChunk::contains - query whether a memory chunk contains a particular offset
2796797ed33SJacob Faibussowitsch 
2806797ed33SJacob Faibussowitsch   Input Parameters:
2816797ed33SJacob Faibussowitsch . offset - The offset from the MemoryBlock start
2826797ed33SJacob Faibussowitsch 
2836797ed33SJacob Faibussowitsch   Notes:
2846797ed33SJacob Faibussowitsch   Returns true if the chunk contains the offset, false otherwise
2856797ed33SJacob Faibussowitsch */
2866797ed33SJacob Faibussowitsch template <typename E>
contains(size_type offset) const287d71ae5a4SJacob Faibussowitsch inline bool MemoryChunk<E>::contains(size_type offset) const noexcept
288d71ae5a4SJacob Faibussowitsch {
2896797ed33SJacob Faibussowitsch   return (offset >= start()) && (offset < total_offset());
2906797ed33SJacob Faibussowitsch }
2916797ed33SJacob Faibussowitsch 
2920e6b6b59SJacob Faibussowitsch // ==========================================================================================
2930e6b6b59SJacob Faibussowitsch // MemoryBlock
2940e6b6b59SJacob Faibussowitsch //
2950e6b6b59SJacob Faibussowitsch // A "memory block" manager, which owns the pointer to a particular memory range. Retrieving
2960e6b6b59SJacob Faibussowitsch // and restoring a block is thread-safe (so may be used by multiple device streams).
2970e6b6b59SJacob Faibussowitsch // ==========================================================================================
2980e6b6b59SJacob Faibussowitsch 
2990e6b6b59SJacob Faibussowitsch template <typename T, typename AllocatorType, typename StreamType>
3000e6b6b59SJacob Faibussowitsch class MemoryBlock {
3010e6b6b59SJacob Faibussowitsch public:
3020e6b6b59SJacob Faibussowitsch   using value_type      = T;
3030e6b6b59SJacob Faibussowitsch   using allocator_type  = AllocatorType;
3040e6b6b59SJacob Faibussowitsch   using stream_type     = StreamType;
3050e6b6b59SJacob Faibussowitsch   using event_type      = typename stream_type::event_type;
3060e6b6b59SJacob Faibussowitsch   using chunk_type      = MemoryChunk<event_type>;
3070e6b6b59SJacob Faibussowitsch   using size_type       = typename chunk_type::size_type;
3080e6b6b59SJacob Faibussowitsch   using chunk_list_type = std::vector<chunk_type>;
3090e6b6b59SJacob Faibussowitsch 
3100e6b6b59SJacob Faibussowitsch   template <typename U>
3110e6b6b59SJacob Faibussowitsch   MemoryBlock(allocator_type *, size_type, const device::StreamBase<U> *) noexcept;
3120e6b6b59SJacob Faibussowitsch 
3130e6b6b59SJacob Faibussowitsch   ~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value);
3140e6b6b59SJacob Faibussowitsch 
3150e6b6b59SJacob Faibussowitsch   MemoryBlock(MemoryBlock &&) noexcept;
3160e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(MemoryBlock &&) noexcept;
3170e6b6b59SJacob Faibussowitsch 
3180e6b6b59SJacob Faibussowitsch   // memory blocks are not copyable
3190e6b6b59SJacob Faibussowitsch   MemoryBlock(const MemoryBlock &)            = delete;
3200e6b6b59SJacob Faibussowitsch   MemoryBlock &operator=(const MemoryBlock &) = delete;
3210e6b6b59SJacob Faibussowitsch 
3220e6b6b59SJacob Faibussowitsch   /* --- actual functions --- */
323089fb57cSJacob Faibussowitsch   PetscErrorCode       try_allocate_chunk(size_type, T **, const stream_type *, bool *) noexcept;
324089fb57cSJacob Faibussowitsch   PetscErrorCode       try_deallocate_chunk(T **, const stream_type *, bool *) noexcept;
325089fb57cSJacob Faibussowitsch   PetscErrorCode       try_find_chunk(const T *, chunk_type **) noexcept;
3260e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD bool owns_pointer(const T *) const noexcept;
3270e6b6b59SJacob Faibussowitsch 
size() const3280e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type size() const noexcept { return size_; }
bytes() const3290e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type bytes() const noexcept { return sizeof(value_type) * size(); }
num_chunks() const3300e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD size_type num_chunks() const noexcept { return chunks_.size(); }
3310e6b6b59SJacob Faibussowitsch 
3320e6b6b59SJacob Faibussowitsch private:
3330e6b6b59SJacob Faibussowitsch   value_type     *mem_{};
3340e6b6b59SJacob Faibussowitsch   allocator_type *allocator_{};
3350e6b6b59SJacob Faibussowitsch   size_type       size_{};
3360e6b6b59SJacob Faibussowitsch   chunk_list_type chunks_{};
3370e6b6b59SJacob Faibussowitsch 
338089fb57cSJacob Faibussowitsch   PetscErrorCode clear_(const stream_type *) noexcept;
3390e6b6b59SJacob Faibussowitsch };
3400e6b6b59SJacob Faibussowitsch 
3410e6b6b59SJacob Faibussowitsch // ==========================================================================================
3420e6b6b59SJacob Faibussowitsch // MemoryBlock - Private API
3430e6b6b59SJacob Faibussowitsch // ==========================================================================================
3440e6b6b59SJacob Faibussowitsch 
3450e6b6b59SJacob Faibussowitsch // clear the memory block, called from destructors and move assignment/construction
3460e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
clear_(const stream_type * stream)347089fb57cSJacob Faibussowitsch PetscErrorCode MemoryBlock<T, A, S>::clear_(const stream_type *stream) noexcept
348d71ae5a4SJacob Faibussowitsch {
3490e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3500e6b6b59SJacob Faibussowitsch   if (PetscLikely(mem_)) {
3510e6b6b59SJacob Faibussowitsch     PetscCall(allocator_->deallocate(mem_, stream));
3520e6b6b59SJacob Faibussowitsch     mem_ = nullptr;
3530e6b6b59SJacob Faibussowitsch   }
3540e6b6b59SJacob Faibussowitsch   size_ = 0;
3550e6b6b59SJacob Faibussowitsch   PetscCallCXX(chunks_.clear());
3563ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3570e6b6b59SJacob Faibussowitsch }
3580e6b6b59SJacob Faibussowitsch 
3590e6b6b59SJacob Faibussowitsch // ==========================================================================================
3600e6b6b59SJacob Faibussowitsch // MemoryBlock - Public API
3610e6b6b59SJacob Faibussowitsch // ==========================================================================================
3620e6b6b59SJacob Faibussowitsch 
3630e6b6b59SJacob Faibussowitsch // default constructor, allocates memory immediately
3640e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
3650e6b6b59SJacob Faibussowitsch template <typename U>
MemoryBlock(allocator_type * alloc,size_type s,const device::StreamBase<U> * stream)366d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::MemoryBlock(allocator_type *alloc, size_type s, const device::StreamBase<U> *stream) noexcept : allocator_(alloc), size_(s)
367d71ae5a4SJacob Faibussowitsch {
3680e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3690e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, alloc->allocate(&mem_, s, stream));
3700e6b6b59SJacob Faibussowitsch   PetscAssertAbort(mem_, PETSC_COMM_SELF, PETSC_ERR_MEM, "Failed to allocate memory block of size %zu", s);
3710e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3720e6b6b59SJacob Faibussowitsch }
3730e6b6b59SJacob Faibussowitsch 
3740e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
~MemoryBlock()375d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S>::~MemoryBlock() noexcept(std::is_nothrow_destructible<chunk_list_type>::value)
376d71ae5a4SJacob Faibussowitsch {
3770e6b6b59SJacob Faibussowitsch   stream_type stream;
3780e6b6b59SJacob Faibussowitsch 
3790e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3800e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
3810e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3820e6b6b59SJacob Faibussowitsch }
3830e6b6b59SJacob Faibussowitsch 
3840e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
MemoryBlock(MemoryBlock && other)385d71ae5a4SJacob 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_))
386d71ae5a4SJacob Faibussowitsch {
387d71ae5a4SJacob Faibussowitsch }
3880e6b6b59SJacob Faibussowitsch 
3890e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
operator =(MemoryBlock && other)390d71ae5a4SJacob Faibussowitsch MemoryBlock<T, A, S> &MemoryBlock<T, A, S>::operator=(MemoryBlock &&other) noexcept
391d71ae5a4SJacob Faibussowitsch {
3920e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3930e6b6b59SJacob Faibussowitsch   if (this != &other) {
3940e6b6b59SJacob Faibussowitsch     stream_type stream;
3950e6b6b59SJacob Faibussowitsch 
3960e6b6b59SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, clear_(&stream));
3970e6b6b59SJacob Faibussowitsch     mem_       = util::exchange(other.mem_, nullptr);
3980e6b6b59SJacob Faibussowitsch     allocator_ = other.allocator_;
3990e6b6b59SJacob Faibussowitsch     size_      = util::exchange(other.size_, 0);
4000e6b6b59SJacob Faibussowitsch     chunks_    = std::move(other.chunks_);
4010e6b6b59SJacob Faibussowitsch   }
4020e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
4030e6b6b59SJacob Faibussowitsch }
4040e6b6b59SJacob Faibussowitsch 
4050e6b6b59SJacob Faibussowitsch /*
4060e6b6b59SJacob Faibussowitsch   MemoryBock::owns_pointer - returns true if this block owns a pointer, false otherwise
4070e6b6b59SJacob Faibussowitsch */
4080e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
owns_pointer(const T * ptr) const409d71ae5a4SJacob Faibussowitsch inline bool MemoryBlock<T, A, S>::owns_pointer(const T *ptr) const noexcept
410d71ae5a4SJacob Faibussowitsch {
4110e6b6b59SJacob Faibussowitsch   // each pool is linear in memory, so it suffices to check the bounds
4120e6b6b59SJacob Faibussowitsch   return (ptr >= mem_) && (ptr < std::next(mem_, size()));
4130e6b6b59SJacob Faibussowitsch }
4140e6b6b59SJacob Faibussowitsch 
4150e6b6b59SJacob Faibussowitsch /*
4160e6b6b59SJacob Faibussowitsch   MemoryBlock::try_allocate_chunk - try to get a chunk from this MemoryBlock
4170e6b6b59SJacob Faibussowitsch 
4180e6b6b59SJacob Faibussowitsch   Input Parameters:
4190e6b6b59SJacob Faibussowitsch + req_size - the requested size of the allocation (in elements)
4200e6b6b59SJacob Faibussowitsch . ptr      - ptr to fill
4210e6b6b59SJacob Faibussowitsch - stream   - stream to fill the pointer on
4220e6b6b59SJacob Faibussowitsch 
4230e6b6b59SJacob Faibussowitsch   Output Parameter:
4240e6b6b59SJacob Faibussowitsch . success  - true if chunk was gotten, false otherwise
4250e6b6b59SJacob Faibussowitsch 
4260e6b6b59SJacob Faibussowitsch   Notes:
4270e6b6b59SJacob Faibussowitsch   If the current memory could not satisfy the memory request, ptr is unchanged
4280e6b6b59SJacob Faibussowitsch */
4290e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
try_allocate_chunk(size_type req_size,T ** ptr,const stream_type * stream,bool * success)430d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_allocate_chunk(size_type req_size, T **ptr, const stream_type *stream, bool *success) noexcept
431d71ae5a4SJacob Faibussowitsch {
4320e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4330e6b6b59SJacob Faibussowitsch   *success = false;
4340e6b6b59SJacob Faibussowitsch   if (req_size <= size()) {
4350e6b6b59SJacob Faibussowitsch     const auto try_create_chunk = [&]() {
4360e6b6b59SJacob Faibussowitsch       const auto was_empty     = chunks_.empty();
4370e6b6b59SJacob Faibussowitsch       const auto block_alloced = was_empty ? 0 : chunks_.back().total_offset();
4380e6b6b59SJacob Faibussowitsch 
4390e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4400e6b6b59SJacob Faibussowitsch       if (block_alloced + req_size <= size()) {
4410e6b6b59SJacob Faibussowitsch         PetscCallCXX(chunks_.emplace_back(block_alloced, req_size));
4420e6b6b59SJacob Faibussowitsch         PetscCall(chunks_.back().claim(stream, req_size, success));
4430e6b6b59SJacob Faibussowitsch         *ptr = mem_ + block_alloced;
4440e6b6b59SJacob 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());
4450e6b6b59SJacob Faibussowitsch       }
4463ba16761SJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
4470e6b6b59SJacob Faibussowitsch     };
4480e6b6b59SJacob Faibussowitsch     const auto try_find_open_chunk = [&](bool serialize = false) {
4490e6b6b59SJacob Faibussowitsch       PetscFunctionBegin;
4500e6b6b59SJacob Faibussowitsch       for (auto &chunk : chunks_) {
4510e6b6b59SJacob Faibussowitsch         PetscCall(chunk.claim(stream, req_size, success, serialize));
4520e6b6b59SJacob Faibussowitsch         if (*success) {
4530e6b6b59SJacob Faibussowitsch           *ptr = mem_ + chunk.start();
4540e6b6b59SJacob Faibussowitsch           break;
4550e6b6b59SJacob Faibussowitsch         }
4560e6b6b59SJacob Faibussowitsch       }
4573ba16761SJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
4580e6b6b59SJacob Faibussowitsch     };
4596797ed33SJacob Faibussowitsch     const auto try_steal_other_stream_chunk = [&]() {
4606797ed33SJacob Faibussowitsch       PetscFunctionBegin;
4616797ed33SJacob Faibussowitsch       PetscCall(try_find_open_chunk(true));
4623ba16761SJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
4636797ed33SJacob Faibussowitsch     };
4640e6b6b59SJacob Faibussowitsch 
4650e6b6b59SJacob Faibussowitsch     // search previously distributed chunks, but only claim one if it is on the same stream
4660e6b6b59SJacob Faibussowitsch     // as us
4670e6b6b59SJacob Faibussowitsch     PetscCall(try_find_open_chunk());
4680e6b6b59SJacob Faibussowitsch 
4690e6b6b59SJacob Faibussowitsch     // if we are here we couldn't reuse one of our own chunks so check first if the pool
4700e6b6b59SJacob Faibussowitsch     // has room for a new one
4710e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4720e6b6b59SJacob Faibussowitsch 
4730e6b6b59SJacob Faibussowitsch     // try pruning dead chunks off the back, note we do this regardless of whether we are
4740e6b6b59SJacob Faibussowitsch     // successful
4750e6b6b59SJacob Faibussowitsch     while (chunks_.back().can_claim(stream, 0, false)) {
4760e6b6b59SJacob Faibussowitsch       PetscCallCXX(chunks_.pop_back());
4770e6b6b59SJacob Faibussowitsch       if (chunks_.empty()) {
4780e6b6b59SJacob Faibussowitsch         // if chunks are empty it implies we have managed to claim (and subsequently destroy)
4790e6b6b59SJacob Faibussowitsch         // our own chunk twice! something has gone wrong
4800e6b6b59SJacob 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());
4810e6b6b59SJacob Faibussowitsch         break;
4820e6b6b59SJacob Faibussowitsch       }
4830e6b6b59SJacob Faibussowitsch     }
4840e6b6b59SJacob Faibussowitsch 
4850e6b6b59SJacob Faibussowitsch     // if previously unsuccessful see if enough space has opened up due to pruning. note that
4860e6b6b59SJacob Faibussowitsch     // if the chunk list was emptied from the pruning this call must succeed in allocating a
4870e6b6b59SJacob Faibussowitsch     // chunk, otherwise something is wrong
4880e6b6b59SJacob Faibussowitsch     if (!*success) PetscCall(try_create_chunk());
4890e6b6b59SJacob Faibussowitsch 
4900e6b6b59SJacob Faibussowitsch     // last resort, iterate over all chunks and see if we can steal one by waiting on the
4910e6b6b59SJacob Faibussowitsch     // current owner to finish using it
4926797ed33SJacob Faibussowitsch     if (!*success) PetscCall(try_steal_other_stream_chunk());
4930e6b6b59SJacob Faibussowitsch   }
4943ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4950e6b6b59SJacob Faibussowitsch }
4960e6b6b59SJacob Faibussowitsch 
4970e6b6b59SJacob Faibussowitsch /*
4980e6b6b59SJacob Faibussowitsch   MemoryBlock::try_deallocate_chunk - try to restore a chunk to this MemoryBlock
4990e6b6b59SJacob Faibussowitsch 
5000e6b6b59SJacob Faibussowitsch   Input Parameters:
5010e6b6b59SJacob Faibussowitsch + ptr     - ptr to restore
5020e6b6b59SJacob Faibussowitsch - stream  - stream to restore the pointer on
5030e6b6b59SJacob Faibussowitsch 
5040e6b6b59SJacob Faibussowitsch   Output Parameter:
5050e6b6b59SJacob Faibussowitsch . success - true if chunk was restored, false otherwise
5060e6b6b59SJacob Faibussowitsch 
5070e6b6b59SJacob Faibussowitsch   Notes:
5080e6b6b59SJacob Faibussowitsch   ptr is set to nullptr on successful restore, and is unchanged otherwise. If the ptr is owned
50935cb6cd3SPierre Jolivet   by this MemoryBlock then it is restored on stream. The same stream may receive ptr again
5100e6b6b59SJacob Faibussowitsch   without synchronization, but other streams may not do so until either serializing or the
5110e6b6b59SJacob Faibussowitsch   stream is idle again.
5120e6b6b59SJacob Faibussowitsch */
5130e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
try_deallocate_chunk(T ** ptr,const stream_type * stream,bool * success)514d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_deallocate_chunk(T **ptr, const stream_type *stream, bool *success) noexcept
515d71ae5a4SJacob Faibussowitsch {
5160e6b6b59SJacob Faibussowitsch   chunk_type *chunk = nullptr;
5170e6b6b59SJacob Faibussowitsch 
5180e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5190e6b6b59SJacob Faibussowitsch   PetscCall(try_find_chunk(*ptr, &chunk));
5200e6b6b59SJacob Faibussowitsch   if (chunk) {
5210e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
5220e6b6b59SJacob Faibussowitsch     *ptr     = nullptr;
5230e6b6b59SJacob Faibussowitsch     *success = true;
5240e6b6b59SJacob Faibussowitsch   } else {
5250e6b6b59SJacob Faibussowitsch     *success = false;
5260e6b6b59SJacob Faibussowitsch   }
5273ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5280e6b6b59SJacob Faibussowitsch }
5290e6b6b59SJacob Faibussowitsch 
5300e6b6b59SJacob Faibussowitsch /*
5310e6b6b59SJacob Faibussowitsch   MemoryBlock::try_find_chunk - try to find the chunk which owns ptr
5320e6b6b59SJacob Faibussowitsch 
5330e6b6b59SJacob Faibussowitsch   Input Parameter:
534146a86ebSJacob Faibussowitsch . ptr - the pointer to look for
5350e6b6b59SJacob Faibussowitsch 
5360e6b6b59SJacob Faibussowitsch   Output Parameter:
5370e6b6b59SJacob Faibussowitsch . ret_chunk - pointer to the owning chunk or nullptr if not found
5380e6b6b59SJacob Faibussowitsch */
5390e6b6b59SJacob Faibussowitsch template <typename T, typename A, typename S>
try_find_chunk(const T * ptr,chunk_type ** ret_chunk)540d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MemoryBlock<T, A, S>::try_find_chunk(const T *ptr, chunk_type **ret_chunk) noexcept
541d71ae5a4SJacob Faibussowitsch {
5420e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5430e6b6b59SJacob Faibussowitsch   *ret_chunk = nullptr;
5440e6b6b59SJacob Faibussowitsch   if (owns_pointer(ptr)) {
5450e6b6b59SJacob Faibussowitsch     const auto offset = static_cast<size_type>(ptr - mem_);
5460e6b6b59SJacob Faibussowitsch 
5470e6b6b59SJacob Faibussowitsch     for (auto &chunk : chunks_) {
5486797ed33SJacob Faibussowitsch       if (chunk.contains(offset)) {
5490e6b6b59SJacob Faibussowitsch         *ret_chunk = &chunk;
5500e6b6b59SJacob Faibussowitsch         break;
5510e6b6b59SJacob Faibussowitsch       }
5520e6b6b59SJacob Faibussowitsch     }
5530e6b6b59SJacob Faibussowitsch 
5540e6b6b59SJacob 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())));
5550e6b6b59SJacob Faibussowitsch   }
5563ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5570e6b6b59SJacob Faibussowitsch }
5580e6b6b59SJacob Faibussowitsch 
559d71ae5a4SJacob Faibussowitsch namespace detail
560d71ae5a4SJacob Faibussowitsch {
5610e6b6b59SJacob Faibussowitsch 
5620e6b6b59SJacob Faibussowitsch template <typename T>
5630e6b6b59SJacob Faibussowitsch struct real_type {
5640e6b6b59SJacob Faibussowitsch   using type = T;
5650e6b6b59SJacob Faibussowitsch };
5660e6b6b59SJacob Faibussowitsch 
5670e6b6b59SJacob Faibussowitsch template <>
5680e6b6b59SJacob Faibussowitsch struct real_type<PetscScalar> {
5690e6b6b59SJacob Faibussowitsch   using type = PetscReal;
5700e6b6b59SJacob Faibussowitsch };
5710e6b6b59SJacob Faibussowitsch 
5720e6b6b59SJacob Faibussowitsch } // namespace detail
5730e6b6b59SJacob Faibussowitsch 
5740e6b6b59SJacob Faibussowitsch template <typename T>
5750e6b6b59SJacob Faibussowitsch struct SegmentedMemoryPoolAllocatorBase {
5760e6b6b59SJacob Faibussowitsch   using value_type      = T;
5770e6b6b59SJacob Faibussowitsch   using size_type       = std::size_t;
5780e6b6b59SJacob Faibussowitsch   using real_value_type = typename detail::real_type<T>::type;
5790e6b6b59SJacob Faibussowitsch 
5800e6b6b59SJacob Faibussowitsch   template <typename U>
581089fb57cSJacob Faibussowitsch   static PetscErrorCode allocate(value_type **, size_type, const device::StreamBase<U> *) noexcept;
5820e6b6b59SJacob Faibussowitsch   template <typename U>
583089fb57cSJacob Faibussowitsch   static PetscErrorCode deallocate(value_type *, const device::StreamBase<U> *) noexcept;
5840e6b6b59SJacob Faibussowitsch   template <typename U>
585089fb57cSJacob Faibussowitsch   static PetscErrorCode zero(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5860e6b6b59SJacob Faibussowitsch   template <typename U>
587089fb57cSJacob Faibussowitsch   static PetscErrorCode uninitialized_copy(value_type *, const value_type *, size_type, const device::StreamBase<U> *) noexcept;
5880e6b6b59SJacob Faibussowitsch   template <typename U>
589089fb57cSJacob Faibussowitsch   static PetscErrorCode set_canary(value_type *, size_type, const device::StreamBase<U> *) noexcept;
5900e6b6b59SJacob Faibussowitsch };
5910e6b6b59SJacob Faibussowitsch 
5920e6b6b59SJacob Faibussowitsch template <typename T>
5930e6b6b59SJacob Faibussowitsch template <typename U>
allocate(value_type ** ptr,size_type n,const device::StreamBase<U> *)594d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::allocate(value_type **ptr, size_type n, const device::StreamBase<U> *) noexcept
595d71ae5a4SJacob Faibussowitsch {
5960e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5970e6b6b59SJacob Faibussowitsch   PetscCall(PetscMalloc1(n, ptr));
5983ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5990e6b6b59SJacob Faibussowitsch }
6000e6b6b59SJacob Faibussowitsch 
6010e6b6b59SJacob Faibussowitsch template <typename T>
6020e6b6b59SJacob Faibussowitsch template <typename U>
deallocate(value_type * ptr,const device::StreamBase<U> *)603d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::deallocate(value_type *ptr, const device::StreamBase<U> *) noexcept
604d71ae5a4SJacob Faibussowitsch {
6050e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6060e6b6b59SJacob Faibussowitsch   PetscCall(PetscFree(ptr));
6073ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6080e6b6b59SJacob Faibussowitsch }
6090e6b6b59SJacob Faibussowitsch 
6100e6b6b59SJacob Faibussowitsch template <typename T>
6110e6b6b59SJacob Faibussowitsch template <typename U>
zero(value_type * ptr,size_type n,const device::StreamBase<U> *)612d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::zero(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept
613d71ae5a4SJacob Faibussowitsch {
6140e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6150e6b6b59SJacob Faibussowitsch   PetscCall(PetscArrayzero(ptr, n));
6163ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6170e6b6b59SJacob Faibussowitsch }
6180e6b6b59SJacob Faibussowitsch 
6190e6b6b59SJacob Faibussowitsch template <typename T>
6200e6b6b59SJacob Faibussowitsch template <typename U>
uninitialized_copy(value_type * dest,const value_type * src,size_type n,const device::StreamBase<U> *)621d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::uninitialized_copy(value_type *dest, const value_type *src, size_type n, const device::StreamBase<U> *) noexcept
622d71ae5a4SJacob Faibussowitsch {
6230e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6240e6b6b59SJacob Faibussowitsch   PetscCall(PetscArraycpy(dest, src, n));
6253ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6260e6b6b59SJacob Faibussowitsch }
6270e6b6b59SJacob Faibussowitsch 
6280e6b6b59SJacob Faibussowitsch template <typename T>
6290e6b6b59SJacob Faibussowitsch template <typename U>
set_canary(value_type * ptr,size_type n,const device::StreamBase<U> *)630d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPoolAllocatorBase<T>::set_canary(value_type *ptr, size_type n, const device::StreamBase<U> *) noexcept
631d71ae5a4SJacob Faibussowitsch {
6320e6b6b59SJacob Faibussowitsch   using limit_type            = std::numeric_limits<real_value_type>;
6330e6b6b59SJacob Faibussowitsch   constexpr value_type canary = limit_type::has_signaling_NaN ? limit_type::signaling_NaN() : limit_type::max();
6340e6b6b59SJacob Faibussowitsch 
6350e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6360e6b6b59SJacob Faibussowitsch   for (size_type i = 0; i < n; ++i) ptr[i] = canary;
6373ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
6380e6b6b59SJacob Faibussowitsch }
6390e6b6b59SJacob Faibussowitsch 
6400e6b6b59SJacob Faibussowitsch } // namespace impl
6410e6b6b59SJacob Faibussowitsch 
6420e6b6b59SJacob Faibussowitsch // ==========================================================================================
6430e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool
6446797ed33SJacob Faibussowitsch //
6456797ed33SJacob Faibussowitsch // Stream-aware async memory allocator. Holds a list of memory "blocks" which each control an
6466797ed33SJacob Faibussowitsch // allocated buffer. This buffer is further split into memory "chunks" which control
6476797ed33SJacob Faibussowitsch // consecutive, non-overlapping regions of the block. Chunks may be in 1 of 2 states:
6486797ed33SJacob Faibussowitsch //
6496797ed33SJacob Faibussowitsch // 1. Open:
6506797ed33SJacob Faibussowitsch //    The chunk is free to be claimed by the next suitable allocation request. If the
6516797ed33SJacob Faibussowitsch //    allocation request is made on the same stream as the chunk was deallocated on, no
6526797ed33SJacob Faibussowitsch //    serialization needs to occur. If not, the allocating stream must wait for the
6536797ed33SJacob Faibussowitsch //    event. Claiming the chunk "closes" the chunk.
6546797ed33SJacob Faibussowitsch //
6556797ed33SJacob Faibussowitsch // 2. Closed:
6566797ed33SJacob Faibussowitsch //    The chunk has been claimed by an allocation request. It cannot be opened again until it
6576797ed33SJacob Faibussowitsch //    is deallocated; doing so "opens" the chunk.
6586797ed33SJacob Faibussowitsch //
6596797ed33SJacob Faibussowitsch // Note that there does not need to be a chunk for every region, chunks are created to satisfy
6606797ed33SJacob Faibussowitsch // an allocation request.
6616797ed33SJacob Faibussowitsch //
6626797ed33SJacob Faibussowitsch // Thus there is usually a region of "unallocated" memory at the end of the buffer, which may
6636797ed33SJacob Faibussowitsch // be claimed by a newly created chunk if existing chunks cannot satisfy the allocation
6646797ed33SJacob Faibussowitsch // request. This region exists _only_ at the end, as there are no gaps between chunks.
6656797ed33SJacob Faibussowitsch //
6666797ed33SJacob Faibussowitsch //
6676797ed33SJacob Faibussowitsch // |-----------------------------------------------------------------------------------------
6686797ed33SJacob Faibussowitsch // | SegmentedMemoryPool
6696797ed33SJacob Faibussowitsch // |
6706797ed33SJacob Faibussowitsch // | ||-------------||
6716797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
6726797ed33SJacob Faibussowitsch // | ||             ||    | AAAAAAAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDXXXXXXXX...
6736797ed33SJacob Faibussowitsch // | ||             ||    | |             |      |                   |            |
6746797ed33SJacob Faibussowitsch // | ||             ||    | x-----x-------x-----xx---------x---------x------x-----x
6756797ed33SJacob Faibussowitsch // | || MemoryBlock || -> | ------|-------------|----------|----------------|--------
6766797ed33SJacob Faibussowitsch // | ||             ||    | | MemoryChunk | MemoryChunk | MemoryChunk | MemoryChunk |
6776797ed33SJacob Faibussowitsch // | ||             ||    | ---------------------------------------------------------
6786797ed33SJacob Faibussowitsch // | ||             ||    -------------------------------------------------------------------
6796797ed33SJacob Faibussowitsch // | ||-------------||
6806797ed33SJacob Faibussowitsch // | ||             ||
6816797ed33SJacob Faibussowitsch // | ||     ...     ||
6826797ed33SJacob Faibussowitsch // | ||             ||
6830e6b6b59SJacob Faibussowitsch // ==========================================================================================
6840e6b6b59SJacob Faibussowitsch 
6850e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType = device::DefaultStream, typename AllocType = impl::SegmentedMemoryPoolAllocatorBase<MemType>, std::size_t DefaultChunkSize = 256>
6860e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool;
6870e6b6b59SJacob Faibussowitsch 
6880e6b6b59SJacob Faibussowitsch // The actual memory pool class. It is in essence just a wrapper for a list of MemoryBlocks.
6890e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
6900e6b6b59SJacob Faibussowitsch class SegmentedMemoryPool : public RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>> {
6910e6b6b59SJacob Faibussowitsch public:
6920e6b6b59SJacob Faibussowitsch   using value_type     = MemType;
6930e6b6b59SJacob Faibussowitsch   using stream_type    = StreamType;
6940e6b6b59SJacob Faibussowitsch   using allocator_type = AllocType;
6950e6b6b59SJacob Faibussowitsch   using block_type     = impl::MemoryBlock<value_type, allocator_type, stream_type>;
6960e6b6b59SJacob Faibussowitsch   using pool_type      = std::deque<block_type>;
6970e6b6b59SJacob Faibussowitsch   using size_type      = typename block_type::size_type;
6980e6b6b59SJacob Faibussowitsch 
6990e6b6b59SJacob Faibussowitsch   explicit SegmentedMemoryPool(AllocType = AllocType{}, std::size_t = DefaultChunkSize) noexcept(std::is_nothrow_default_constructible<pool_type>::value);
7000e6b6b59SJacob Faibussowitsch 
701*31a765c4SPierre Jolivet   PetscErrorCode allocate(size_t, value_type **, const stream_type *, size_type = std::alignment_of<MemType>::value) noexcept;
702089fb57cSJacob Faibussowitsch   PetscErrorCode deallocate(value_type **, const stream_type *) noexcept;
703*31a765c4SPierre Jolivet   PetscErrorCode reallocate(size_t, value_type **, const stream_type *) noexcept;
7040e6b6b59SJacob Faibussowitsch 
7050e6b6b59SJacob Faibussowitsch private:
7060e6b6b59SJacob Faibussowitsch   pool_type      pool_;
7070e6b6b59SJacob Faibussowitsch   allocator_type allocator_;
7080e6b6b59SJacob Faibussowitsch   size_type      chunk_size_;
7090e6b6b59SJacob Faibussowitsch 
710089fb57cSJacob Faibussowitsch   PetscErrorCode make_block_(size_type, const stream_type *) noexcept;
7110e6b6b59SJacob Faibussowitsch 
7120e6b6b59SJacob Faibussowitsch   friend class RegisterFinalizeable<SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>>;
713089fb57cSJacob Faibussowitsch   PetscErrorCode register_finalize_(const stream_type *) noexcept;
714089fb57cSJacob Faibussowitsch   PetscErrorCode finalize_() noexcept;
7156797ed33SJacob Faibussowitsch 
716089fb57cSJacob Faibussowitsch   PetscErrorCode allocate_(size_type, value_type **, const stream_type *) noexcept;
7170e6b6b59SJacob Faibussowitsch };
7180e6b6b59SJacob Faibussowitsch 
7190e6b6b59SJacob Faibussowitsch // ==========================================================================================
7200e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Private API
7210e6b6b59SJacob Faibussowitsch // ==========================================================================================
7220e6b6b59SJacob Faibussowitsch 
7230e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
make_block_(size_type size,const stream_type * stream)724d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::make_block_(size_type size, const stream_type *stream) noexcept
725d71ae5a4SJacob Faibussowitsch {
7260e6b6b59SJacob Faibussowitsch   const auto block_size = std::max(size, chunk_size_);
7270e6b6b59SJacob Faibussowitsch 
7280e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7290e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.emplace_back(&allocator_, block_size, stream));
7300e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Allocated new block of size %zu, total %zu blocks\n", block_size, pool_.size()));
7313ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7320e6b6b59SJacob Faibussowitsch }
7330e6b6b59SJacob Faibussowitsch 
7340e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
register_finalize_(const stream_type * stream)735d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::register_finalize_(const stream_type *stream) noexcept
736d71ae5a4SJacob Faibussowitsch {
7370e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7380e6b6b59SJacob Faibussowitsch   PetscCall(make_block_(chunk_size_, stream));
7393ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7400e6b6b59SJacob Faibussowitsch }
7410e6b6b59SJacob Faibussowitsch 
7420e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
finalize_()743d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::finalize_() noexcept
744d71ae5a4SJacob Faibussowitsch {
7450e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
7460e6b6b59SJacob Faibussowitsch   PetscCallCXX(pool_.clear());
7470e6b6b59SJacob Faibussowitsch   chunk_size_ = DefaultChunkSize;
7483ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7490e6b6b59SJacob Faibussowitsch }
7500e6b6b59SJacob Faibussowitsch 
7516797ed33SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
allocate_(size_type size,value_type ** ptr,const stream_type * stream)752d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate_(size_type size, value_type **ptr, const stream_type *stream) noexcept
753d71ae5a4SJacob Faibussowitsch {
7546797ed33SJacob Faibussowitsch   auto found = false;
7556797ed33SJacob Faibussowitsch 
7566797ed33SJacob Faibussowitsch   PetscFunctionBegin;
757503aa7efSJacob Faibussowitsch   PetscCall(this->register_finalize(stream));
7586797ed33SJacob Faibussowitsch   for (auto &block : pool_) {
7596797ed33SJacob Faibussowitsch     PetscCall(block.try_allocate_chunk(size, ptr, stream, &found));
7603ba16761SJacob Faibussowitsch     if (PetscLikely(found)) PetscFunctionReturn(PETSC_SUCCESS);
7616797ed33SJacob Faibussowitsch   }
7626797ed33SJacob Faibussowitsch 
7636797ed33SJacob 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));
7646797ed33SJacob Faibussowitsch   // if we are here we couldn't find an open block in the pool, so make a new block
7656797ed33SJacob Faibussowitsch   PetscCall(make_block_(size, stream));
7666797ed33SJacob Faibussowitsch   // and assign it
7676797ed33SJacob Faibussowitsch   PetscCall(pool_.back().try_allocate_chunk(size, ptr, stream, &found));
7686797ed33SJacob 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());
7693ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7706797ed33SJacob Faibussowitsch }
7716797ed33SJacob Faibussowitsch 
7720e6b6b59SJacob Faibussowitsch // ==========================================================================================
7730e6b6b59SJacob Faibussowitsch // SegmentedMemoryPool - Public API
7740e6b6b59SJacob Faibussowitsch // ==========================================================================================
7750e6b6b59SJacob Faibussowitsch 
7760e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
SegmentedMemoryPool(AllocType alloc,std::size_t size)777d71ae5a4SJacob 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)
778d71ae5a4SJacob Faibussowitsch {
779d71ae5a4SJacob Faibussowitsch }
7800e6b6b59SJacob Faibussowitsch 
7810e6b6b59SJacob Faibussowitsch /*
7820e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::allocate - get an allocation from the memory pool
7830e6b6b59SJacob Faibussowitsch 
7840e6b6b59SJacob Faibussowitsch   Input Parameters:
7850e6b6b59SJacob Faibussowitsch + req_size - size (in elements) to get
7860e6b6b59SJacob Faibussowitsch . ptr      - the pointer to hold the allocation
7870e6b6b59SJacob Faibussowitsch - stream   - the stream on which to get the allocation
7880e6b6b59SJacob Faibussowitsch 
7890e6b6b59SJacob Faibussowitsch   Output Parameter:
7900e6b6b59SJacob Faibussowitsch . ptr - the pointer holding the allocation
7910e6b6b59SJacob Faibussowitsch 
7920e6b6b59SJacob Faibussowitsch   Notes:
79376c63389SBarry Smith   `req_size` cannot be negative. If `req_size` if zero, `ptr` is set to `nullptr`
7940e6b6b59SJacob Faibussowitsch */
7950e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
allocate(size_t req_size,value_type ** ptr,const stream_type * stream,size_type alignment)796*31a765c4SPierre Jolivet inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::allocate(size_t req_size, value_type **ptr, const stream_type *stream, size_type alignment) noexcept
797d71ae5a4SJacob Faibussowitsch {
7986797ed33SJacob Faibussowitsch   value_type *ret_ptr = nullptr;
7990e6b6b59SJacob Faibussowitsch 
8000e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8014f572ea9SToby Isaac   PetscAssertPointer(ptr, 2);
8024f572ea9SToby Isaac   PetscAssertPointer(stream, 3);
8036797ed33SJacob Faibussowitsch   if (req_size) {
8046797ed33SJacob Faibussowitsch     const auto size         = static_cast<size_type>(req_size);
8056797ed33SJacob Faibussowitsch     auto       aligned_size = alignment == alignof(char) ? size : size + alignment;
8066797ed33SJacob Faibussowitsch     void      *vptr         = nullptr;
8070e6b6b59SJacob Faibussowitsch 
8086797ed33SJacob Faibussowitsch     PetscCall(allocate_(aligned_size, &ret_ptr, stream));
8096797ed33SJacob Faibussowitsch     vptr = ret_ptr;
8106797ed33SJacob Faibussowitsch     std::align(alignment, size, vptr, aligned_size);
8116797ed33SJacob Faibussowitsch     ret_ptr = reinterpret_cast<value_type *>(vptr);
81276c63389SBarry Smith     // sets memory to infinity or NaN depending on the type to catch out uninitialized memory accesses.
8136797ed33SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG)) PetscCall(allocator_.set_canary(ret_ptr, size, stream));
8146797ed33SJacob Faibussowitsch   }
8156797ed33SJacob Faibussowitsch   *ptr = ret_ptr;
8163ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8170e6b6b59SJacob Faibussowitsch }
8180e6b6b59SJacob Faibussowitsch 
8190e6b6b59SJacob Faibussowitsch /*
8200e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::deallocate - release a pointer back to the memory pool
8210e6b6b59SJacob Faibussowitsch 
8220e6b6b59SJacob Faibussowitsch   Input Parameters:
8230e6b6b59SJacob Faibussowitsch + ptr    - the pointer to release
8240e6b6b59SJacob Faibussowitsch - stream - the stream to release it on
8250e6b6b59SJacob Faibussowitsch 
8260e6b6b59SJacob Faibussowitsch   Notes:
82776c63389SBarry Smith   If `ptr` is not owned by the pool it is unchanged.
8280e6b6b59SJacob Faibussowitsch */
8290e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
deallocate(value_type ** ptr,const stream_type * stream)830d71ae5a4SJacob Faibussowitsch inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::deallocate(value_type **ptr, const stream_type *stream) noexcept
831d71ae5a4SJacob Faibussowitsch {
8320e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8334f572ea9SToby Isaac   PetscAssertPointer(ptr, 1);
8344f572ea9SToby Isaac   PetscAssertPointer(stream, 2);
8350e6b6b59SJacob Faibussowitsch   // nobody owns a nullptr, and if they do then they have bigger problems
8363ba16761SJacob Faibussowitsch   if (!*ptr) PetscFunctionReturn(PETSC_SUCCESS);
8370e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8380e6b6b59SJacob Faibussowitsch     auto found = false;
8390e6b6b59SJacob Faibussowitsch 
8400e6b6b59SJacob Faibussowitsch     PetscCall(block.try_deallocate_chunk(ptr, stream, &found));
8410e6b6b59SJacob Faibussowitsch     if (PetscLikely(found)) break;
8420e6b6b59SJacob Faibussowitsch   }
8433ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8440e6b6b59SJacob Faibussowitsch }
8450e6b6b59SJacob Faibussowitsch 
8460e6b6b59SJacob Faibussowitsch /*
8470e6b6b59SJacob Faibussowitsch   SegmentedMemoryPool::reallocate - Resize an allocated buffer
8480e6b6b59SJacob Faibussowitsch 
8490e6b6b59SJacob Faibussowitsch   Input Parameters:
8500e6b6b59SJacob Faibussowitsch + new_req_size - the new buffer size
8510e6b6b59SJacob Faibussowitsch . ptr          - pointer to the buffer
8520e6b6b59SJacob Faibussowitsch - stream       - stream to resize with
8530e6b6b59SJacob Faibussowitsch 
854d5b43468SJose E. Roman   Output Parameter:
8550e6b6b59SJacob Faibussowitsch . ptr - pointer to the new region
8560e6b6b59SJacob Faibussowitsch 
8570e6b6b59SJacob Faibussowitsch   Notes:
85876c63389SBarry Smith   `ptr` must have been allocated by the pool.
8590e6b6b59SJacob Faibussowitsch 
8600e6b6b59SJacob Faibussowitsch   It's OK to shrink the buffer, even down to 0 (in which case it is just deallocated).
8610e6b6b59SJacob Faibussowitsch */
8620e6b6b59SJacob Faibussowitsch template <typename MemType, typename StreamType, typename AllocType, std::size_t DefaultChunkSize>
reallocate(size_t new_req_size,value_type ** ptr,const stream_type * stream)863*31a765c4SPierre Jolivet inline PetscErrorCode SegmentedMemoryPool<MemType, StreamType, AllocType, DefaultChunkSize>::reallocate(size_t new_req_size, value_type **ptr, const stream_type *stream) noexcept
864d71ae5a4SJacob Faibussowitsch {
8650e6b6b59SJacob Faibussowitsch   using chunk_type = typename block_type::chunk_type;
8660e6b6b59SJacob Faibussowitsch 
8670e6b6b59SJacob Faibussowitsch   const auto  new_size = static_cast<size_type>(new_req_size);
8680e6b6b59SJacob Faibussowitsch   const auto  old_ptr  = *ptr;
8690e6b6b59SJacob Faibussowitsch   chunk_type *chunk    = nullptr;
8700e6b6b59SJacob Faibussowitsch 
8710e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
8724f572ea9SToby Isaac   PetscAssertPointer(ptr, 2);
8734f572ea9SToby Isaac   PetscAssertPointer(stream, 3);
8740e6b6b59SJacob Faibussowitsch 
8750e6b6b59SJacob Faibussowitsch   // if reallocating to zero, just free
8760e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(new_size == 0)) {
8770e6b6b59SJacob Faibussowitsch     PetscCall(deallocate(ptr, stream));
8783ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
8790e6b6b59SJacob Faibussowitsch   }
8800e6b6b59SJacob Faibussowitsch 
8810e6b6b59SJacob Faibussowitsch   // search the blocks for the owning chunk
8820e6b6b59SJacob Faibussowitsch   for (auto &block : pool_) {
8830e6b6b59SJacob Faibussowitsch     PetscCall(block.try_find_chunk(old_ptr, &chunk));
8840e6b6b59SJacob Faibussowitsch     if (chunk) break; // found
8850e6b6b59SJacob Faibussowitsch   }
8860e6b6b59SJacob Faibussowitsch   PetscAssert(chunk, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Memory pool does not own %p, so cannot reallocate it", *ptr);
8870e6b6b59SJacob Faibussowitsch 
8880e6b6b59SJacob Faibussowitsch   if (chunk->capacity() < new_size) {
8890e6b6b59SJacob Faibussowitsch     // chunk does not have enough room, need to grab a fresh chunk and copy to it
8900e6b6b59SJacob Faibussowitsch     *ptr = nullptr;
8910e6b6b59SJacob Faibussowitsch     PetscCall(chunk->release(stream));
8920e6b6b59SJacob Faibussowitsch     PetscCall(allocate(new_size, ptr, stream));
8930e6b6b59SJacob Faibussowitsch     PetscCall(allocator_.uninitialized_copy(*ptr, old_ptr, new_size, stream));
8940e6b6b59SJacob Faibussowitsch   } else {
8950e6b6b59SJacob Faibussowitsch     // chunk had enough room we can simply grow (or shrink) to fit the new size
8960e6b6b59SJacob Faibussowitsch     PetscCall(chunk->resize(new_size));
8970e6b6b59SJacob Faibussowitsch   }
8983ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8990e6b6b59SJacob Faibussowitsch }
9000e6b6b59SJacob Faibussowitsch 
9010e6b6b59SJacob Faibussowitsch } // namespace memory
9020e6b6b59SJacob Faibussowitsch 
9030e6b6b59SJacob Faibussowitsch } // namespace Petsc
904