DASH  0.3.0
SimpleMemoryPoolResource.h
1 #ifndef DASH__MEMORY__SIMPLE_MEMORY_POOL_RESOURCE_H_
2 #define DASH__MEMORY__SIMPLE_MEMORY_POOL_RESOURCE_H_
3 
4 #include <cstddef>
5 #include <cstdint>
6 #include <memory>
7 
8 #include <dash/Exception.h>
9 #include <dash/Types.h>
10 
11 #include <dash/allocator/AllocatorBase.h>
12 #include <dash/memory/MemorySpace.h>
13 
14 // clang-format off
15 
33 // clang-format on
34 namespace dash {
35 
36 namespace detail {
37 
38 template <size_t Blocksize>
39 union Block {
40  Block* next;
41 
42  std::uint8_t _data[Blocksize];
43 };
44 
45 union Chunk {
46  Chunk* next;
47 };
48 
49 struct Header {
50  std::size_t size;
51 };
52 
53 } // namespace detail
54 
55 template <class LocalMemSpace>
57  : public dash::MemorySpace<
58  memory_domain_local,
59  typename LocalMemSpace::memory_space_type_category> {
60 private:
61  // PRIVATE TYPES
62 
63  static constexpr const size_t MAX_ALIGN = alignof(dash::max_align_t);
64 
65  static constexpr const size_t MAX_BLOCKS_PER_CHUNK = 32;
66 
67  static constexpr const size_t MAX_BLOCK_SIZE = 16;
68 
70 
71  using Block = detail::Block<MAX_BLOCK_SIZE>;
72  using Chunk = detail::Chunk;
73 
74  static_assert(
75  memory_traits::is_local::value, "Upstream Memory Space must be local");
76 
77 public:
81  using memory_space_domain_category =
83 
84 public:
85  // CONSTRUCTOR
86  explicit SimpleMemoryPoolResource(
87  LocalMemSpace* resource = nullptr) noexcept;
88 
89  // COPY CONSTRUCTOR
91 
92  // MOVE CONSTRUCTOR
94 
95  // DELETED MOVE ASSIGNMENT
97  // DELETED COPY ASSIGNMENT
99  delete;
100 
101  ~SimpleMemoryPoolResource() noexcept;
102 
104  inline LocalMemSpace* upstream_resource();
105 
106 private:
107  // provide more space in the pool
108  void refill();
109  // allocate a Chunk for at least nbytes
110  Block* allocateChunk(std::size_t nbytes);
111 
112 protected:
113  void* do_allocate(size_t bytes, size_t alignment) override;
114  void do_deallocate(void* p, size_t bytes, size_t alignment) override;
115  bool do_is_equal(std::pmr::memory_resource const& other) const
116  noexcept override;
117 
118 public:
120  void release();
121 
122  void reserve(std::size_t nblocks);
123 
124 private:
125  Chunk* m_chunklist = nullptr;
126  Block* m_freelist = nullptr;
127  int m_blocks_per_chunk;
128  LocalMemSpace* m_resource;
129 };
130 
131 // CONSTRUCTOR
132 template <class LocalMemSpace>
134  LocalMemSpace* r) noexcept
135  : m_chunklist(nullptr)
136  , m_freelist(nullptr)
137  , m_blocks_per_chunk(1)
138  , m_resource(
139  r ? r
140  : static_cast<LocalMemSpace*>(
144 {
145 }
146 
147 // COPY CONSTRUCTOR
148 template <typename LocalMemSpace>
150  SimpleMemoryPoolResource const& other) noexcept
151  : m_chunklist(nullptr)
152  , m_freelist(nullptr)
153  , m_blocks_per_chunk(other.m_blocks_per_chunk)
154  , m_resource(other.m_resource)
155 {
156 }
157 
158 // MOVE CONSTRUCTOR
159 template <typename LocalMemSpace>
161  SimpleMemoryPoolResource&& other) noexcept
162  : m_chunklist(other.m_chunklist)
163  , m_freelist(other.m_freelist)
164  , m_blocks_per_chunk(other.m_blocks_per_chunk)
165  , m_resource(other.m_resource)
166 {
167  other.m_chunklist = nullptr;
168  other.m_freelist = nullptr;
169  other.m_blocks_per_chunk = 1;
170 }
171 template <typename LocalMemSpace>
173  size_t bytes, size_t alignment)
174 {
175  (void)alignment; // unused here, pools always allocate with alignment
176  // max_align_t
177 
178  if (bytes > MAX_BLOCK_SIZE) {
179  std::size_t const header_sz = sizeof(detail::Header);
180 
181  if (std::size_t(-1) - header_sz < bytes) {
182  throw std::bad_alloc();
183  }
184 
185  detail::Header* p = reinterpret_cast<detail::Header*>(
186  m_resource->allocate(bytes + header_sz, MAX_ALIGN));
187 
188  p->size = bytes + header_sz;
189 
190  return p + 1;
191  }
192 
193  if (!m_freelist) {
194  refill();
195  }
196 
197  void* block = reinterpret_cast<void*>(m_freelist);
198  m_freelist = m_freelist->next;
199  return block;
200 }
201 
202 template <typename LocalMemSpace>
204  void* address, size_t bytes, size_t alignment)
205 {
206  // Unused here
207  (void)alignment;
208 
209  if (bytes > MAX_BLOCK_SIZE) {
210  // m_resource->deallocate(address, bytes, MAX_ALIGN);
211  std::size_t const header_sz = sizeof(detail::Header);
212 
213  auto header = static_cast<detail::Header*>(
214  static_cast<void*>(reinterpret_cast<char*>(address) - header_sz));
215 
216  std::size_t const sz = header->size;
217 
218  m_resource->deallocate(header, sz, MAX_ALIGN);
219  }
220 
221  DASH_ASSERT(address);
222  reinterpret_cast<Block*>(address)->next = m_freelist;
223  m_freelist = reinterpret_cast<Block*>(address);
224 }
225 
226 template <typename LocalMemSpace>
228  std::pmr::memory_resource const& other) const noexcept
229 {
230  const SimpleMemoryPoolResource* other_p =
231  dynamic_cast<const SimpleMemoryPoolResource*>(&other);
232 
233  if (other_p)
234  return this->m_resource == other_p->m_resource;
235  else
236  return false;
237 }
238 
239 template <typename LocalMemSpace>
241  std::size_t nblocks)
242 {
243  // allocate a chunk with header and space for nblocks
244  DASH_ASSERT(0 < nblocks);
245 
246  Block* begin = allocateChunk(nblocks * sizeof(Block));
247  Block* end = begin + nblocks - 1;
248 
249  for (Block* p = begin; p < end; ++p) {
250  p->next = p + 1;
251  }
252  end->next = m_freelist;
253  m_freelist = begin;
254 }
255 
256 template <typename LocalMemSpace>
257 inline LocalMemSpace*
259 {
260  return m_resource;
261 }
262 
263 template <typename LocalMemSpace>
265 {
266  while (m_chunklist) {
267  Chunk* lastChunk = m_chunklist;
268  m_chunklist = m_chunklist->next;
269  m_resource->deallocate(lastChunk, MAX_ALIGN);
270  }
271  m_freelist = 0;
272 }
273 
274 template <typename LocalMemSpace>
276 {
277  // allocate a chunk with header and space for nblocks
278  reserve(m_blocks_per_chunk);
279 
280  // increase blocks per chunk by 2
281  m_blocks_per_chunk *= 2;
282 
283  if (m_blocks_per_chunk > MAX_BLOCKS_PER_CHUNK)
284  m_blocks_per_chunk = MAX_BLOCKS_PER_CHUNK;
285 }
286 
287 template <typename LocalMemSpace>
288 typename SimpleMemoryPoolResource<LocalMemSpace>::Block*
290 {
291  // nbytes == nblocks * sizeof(Block)
292  std::size_t numBytes = sizeof(Chunk) + nbytes;
293 
294  Chunk* chunkPtr =
295  reinterpret_cast<Chunk*>(m_resource->allocate(numBytes, MAX_ALIGN));
296 
297  // append allocated chunks to already existing chunks
298  chunkPtr->next = m_chunklist;
299  m_chunklist = chunkPtr;
300 
301  // We have to increment by 1 since user memory starts at offset 1
302  return reinterpret_cast<Block*>(chunkPtr + 1);
303 }
304 
305 template <typename LocalMemSpace>
307 {
308  release();
309 }
310 
311 } // namespace dash
312 
313 #endif // DASH__MEMORY__SIMPLE_MEMORY_POOL_RESOURCE_H_
MemorySpace< MSpaceDomainCategory, MSpaceTypeCategory > * get_default_memory_space()
Forward declarations.
Definition: MemorySpace.h:29
size_t size()
Return the number of units in the global team.
constexpr auto end(RangeType &&range) -> decltype(std::forward< RangeType >(range).end())
Definition: Range.h:98
This class is a simple memory pool which holds allocates elements of size ValueType.
Definition: AllOf.h:8
constexpr auto begin(RangeType &&range) -> decltype(std::forward< RangeType >(range).begin())
Definition: Range.h:89
typename MemSpace::memory_space_domain_category memory_space_domain_category
The underlying memory domain (local, global, etc.)
LocalMemSpace * upstream_resource()
Returns the underlying memory resource.
void release()
deallocate all memory blocks of all chunks
typename memory_traits::memory_space_type_category memory_space_type_category
Require for memory traits.
see https://en.cppreference.com/w/cpp/feature_test for recommended feature tests
Definition: cstddef.h:8
typename MemSpace::memory_space_type_category memory_space_type_category
The underlying memory type (Host, CUDA, HBW, etc.)
constexpr auto block(OffsetT block_idx, const ViewType &view) -> typename std::enable_if<(!dash::view_traits< ViewType >::is_local::value), ViewBlockMod< ViewType > >::type
Blocks view from global view.
Definition: Chunked.h:49