Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
chunk_allocator.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cinttypes>
5#include <cstdint>
6#include <cstring>
7
8#include "gaia/cnt/fwd_llist.h"
9#include "gaia/cnt/sarray.h"
10#include "gaia/core/bit_utils.h"
11#include "gaia/core/dyn_singleton.h"
12#include "gaia/core/utility.h"
13#include "gaia/mem/mem_alloc.h"
14#include "gaia/util/logging.h"
15
16namespace gaia {
17 namespace ecs {
18 namespace detail {
19 struct MemoryBlockHeader final {
20 uintptr_t m_pageAddr = 0;
21 uint32_t m_reserved = 0;
22#if GAIA_DEBUG
23 uint32_t m_requestedBytes = 0;
24#endif
25 };
26
27 class ChunkAllocatorImpl;
28 } // namespace detail
29
30 static constexpr uint32_t MemoryBlockAlignment = 64;
31 static constexpr uint32_t MinMemoryBlockSize = 1024 * 8;
33 static constexpr uint32_t MemoryBlockSizeClasses = 4;
35 static constexpr uint32_t MaxMemoryBlockSize = UINT16_MAX & ~(MemoryBlockAlignment - 1);
38 static constexpr uint32_t MemoryBlockUsableOffset = 40;
39
40 constexpr uint16_t mem_block_size(uint32_t sizeType) {
41 constexpr uint16_t sizes[] = {
42 MinMemoryBlockSize, MinMemoryBlockSize * 2, MinMemoryBlockSize * 4, MaxMemoryBlockSize};
43 return sizes[sizeType];
44 }
45
46 constexpr uint8_t mem_block_size_type(uint32_t sizeBytes) {
47 GAIA_ASSERT(sizeBytes > 0);
48 if (sizeBytes <= MinMemoryBlockSize)
49 return 0;
50 if (sizeBytes <= MinMemoryBlockSize * 2)
51 return 1;
52 if (sizeBytes <= MinMemoryBlockSize * 4)
53 return 2;
54 return 3;
55 }
56
57#if GAIA_ECS_CHUNK_ALLOCATOR
58 struct GAIA_API ChunkAllocatorPageStats final {
60 uint64_t mem_total;
62 uint64_t mem_used;
64 uint32_t num_pages;
66 uint32_t num_pages_free;
67 #if GAIA_DEBUG
69 uint64_t mem_requested;
71 uint32_t num_pages_empty;
72 #endif
73 };
74
75 struct GAIA_API ChunkAllocatorStats final {
76 ChunkAllocatorPageStats stats[MemoryBlockSizeClasses];
77 };
78
79 using ChunkAllocator = core::dyn_singleton<detail::ChunkAllocatorImpl>;
80
81 namespace detail {
82 static_assert(sizeof(MemoryBlockHeader) <= MemoryBlockUsableOffset);
83
84 struct MemoryPageHeader {
86 void* m_data;
87
88 MemoryPageHeader(void* ptr): m_data(ptr) {}
89 };
90
91 struct MemoryPage: MemoryPageHeader, cnt::fwd_llist_base<MemoryPage> {
92 static constexpr uint16_t NBlocks = 48;
93 static constexpr uint16_t NBlocks_Bits = (uint16_t)core::count_bits(NBlocks);
94 static constexpr uint32_t InvalidBlockId = NBlocks + 1;
95 #if GAIA_DEBUG
96 static constexpr uint8_t FreedBlockPattern = 0xDD;
97 #endif
98 static constexpr uint32_t BlockArrayBytes = ((uint32_t)NBlocks_Bits * (uint32_t)NBlocks + 7) / 8;
99 using BlockArray = cnt::sarray<uint8_t, BlockArrayBytes>;
100 using BitView = core::bit_view<NBlocks_Bits>;
101
103 BlockArray m_blocks;
104
106 uint32_t m_sizeType : 2;
108 uint32_t m_blockCnt : NBlocks_Bits;
110 uint32_t m_usedBlocks : NBlocks_Bits;
112 uint32_t m_nextFreeBlock : NBlocks_Bits;
114 uint32_t m_freeBlocks : NBlocks_Bits;
116 // uint32_t m_unused : 6;
117
118 #if GAIA_ASSERT_ENABLED
119 uint64_t m_usedMask = 0;
120 #endif
121
122 MemoryPage(void* ptr, uint8_t sizeType):
123 MemoryPageHeader(ptr), m_sizeType(sizeType), m_blockCnt(0), m_usedBlocks(0), m_nextFreeBlock(0),
124 m_freeBlocks(0) {
125 #if GAIA_ASSERT_ENABLED
126 static_assert(sizeof(MemoryPage) <= 72);
127 #else
128 static_assert(sizeof(MemoryPage) <= 64);
129 #endif
130 }
131
132 void write_block_idx(uint32_t blockIdx, uint32_t value) {
133 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
134
135 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
136 GAIA_ASSERT(value <= InvalidBlockId);
137
138 BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.set(bitPosition, (uint8_t)value);
139 }
140
141 uint8_t read_block_idx(uint32_t blockIdx) const {
142 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
143
144 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
145
146 return BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.get(bitPosition);
147 }
148
149 GAIA_NODISCARD void* alloc_block(
150 #if GAIA_DEBUG
151 uint32_t bytesWanted
152 #endif
153 ) {
154 auto StoreBlockAddress = [&](uint32_t index) {
155 // Encode info about chunk's page in the memory block.
156 // The actual pointer returned is offset by MemoryBlockUsableOffset bytes
157 auto* pMemoryBlock = (uint8_t*)m_data + (index * mem_block_size(m_sizeType));
158 GAIA_ASSERT((uintptr_t)pMemoryBlock % MemoryBlockAlignment == 0);
159 auto& header = block_header(pMemoryBlock);
160 header.m_pageAddr = (uintptr_t)this;
161 header.m_reserved = 0;
162 #if GAIA_DEBUG
163 header.m_requestedBytes = bytesWanted;
164 #endif
165 return (void*)(pMemoryBlock + MemoryBlockUsableOffset);
166 };
167
168 // We don't want to go out of range for new blocks
169 GAIA_ASSERT(!full() && "Trying to allocate too many blocks!");
170
171 uint32_t index = 0;
172 if (m_freeBlocks == 0U) {
173 index = m_blockCnt;
174 ++m_usedBlocks;
175 ++m_blockCnt;
176 write_block_idx(index, index);
177 } else {
178 GAIA_ASSERT(m_nextFreeBlock < m_blockCnt && "Block allocator recycle list broken!");
179
180 ++m_usedBlocks;
181 --m_freeBlocks;
182
183 index = m_nextFreeBlock;
184 m_nextFreeBlock = read_block_idx(m_nextFreeBlock);
185 }
186
187 #if GAIA_ASSERT_ENABLED
188 GAIA_ASSERT((m_usedMask & (uint64_t(1) << index)) == 0 && "Block already marked as live");
189 m_usedMask |= uint64_t(1) << index;
190 #endif
191
192 return StoreBlockAddress(index);
193 }
194
195 void free_block(void* pBlock) {
196 GAIA_ASSERT(pBlock != nullptr);
197 GAIA_ASSERT(m_usedBlocks > 0);
198 GAIA_ASSERT(m_freeBlocks <= NBlocks);
199
200 // Offset the chunk memory so we get the real block address
201 const auto* pMemoryBlock = (uint8_t*)pBlock - MemoryBlockUsableOffset;
202 const auto blckAddr = (uintptr_t)pMemoryBlock;
203 GAIA_ASSERT(blckAddr % MemoryBlockAlignment == 0);
204 const auto dataAddr = (uintptr_t)m_data;
205 GAIA_ASSERT(blckAddr >= dataAddr);
206 const auto blockSize = (uintptr_t)mem_block_size(m_sizeType);
207 #if GAIA_ASSERT_ENABLED
208 const auto pageSize = blockSize * NBlocks;
209 GAIA_ASSERT(blckAddr < dataAddr + pageSize);
210 #endif
211 GAIA_ASSERT((blckAddr - dataAddr) % blockSize == 0);
212 const auto blockIdx = (uint32_t)((blckAddr - dataAddr) / blockSize);
213 GAIA_ASSERT(blockIdx < m_blockCnt);
214
215 #if GAIA_DEBUG
216 auto& header = block_header((void*)pMemoryBlock);
217 GAIA_ASSERT(header.m_requestedBytes > 0);
218 #endif
219 #if GAIA_ASSERT_ENABLED
220 GAIA_ASSERT((m_usedMask & (uint64_t(1) << blockIdx)) != 0 && "Double free or corrupted block state");
221 m_usedMask &= ~(uint64_t(1) << blockIdx);
222 #endif
223
224 #if GAIA_DEBUG
225 header.m_requestedBytes = 0;
226 std::memset(pBlock, FreedBlockPattern, blockSize - MemoryBlockUsableOffset);
227 #endif
228
229 // Update our implicit list
230 if (m_freeBlocks == 0U)
231 write_block_idx(blockIdx, InvalidBlockId);
232 else
233 write_block_idx(blockIdx, m_nextFreeBlock);
234 m_nextFreeBlock = blockIdx;
235
236 ++m_freeBlocks;
237 --m_usedBlocks;
238 }
239
240 GAIA_NODISCARD uint32_t used_blocks_cnt() const {
241 return m_usedBlocks;
242 }
243
244 GAIA_NODISCARD bool full() const {
245 return used_blocks_cnt() >= NBlocks;
246 }
247
248 GAIA_NODISCARD bool empty() const {
249 return used_blocks_cnt() == 0;
250 }
251
252 void verify() const {
253 #if GAIA_ASSERT_ENABLED
254 GAIA_ASSERT(m_sizeType < MemoryBlockSizeClasses);
255 GAIA_ASSERT(m_blockCnt <= NBlocks);
256 GAIA_ASSERT(m_usedBlocks <= m_blockCnt);
257 GAIA_ASSERT(m_freeBlocks <= m_blockCnt);
258 GAIA_ASSERT(m_usedBlocks + m_freeBlocks == m_blockCnt);
259
260 const auto blockSize = (uintptr_t)mem_block_size(m_sizeType);
261
262 const auto pageAddr = (uintptr_t)m_data;
263 GAIA_ASSERT(pageAddr % MemoryBlockAlignment == 0);
264
265 #if GAIA_DEBUG
266 uint64_t freeMask = 0;
267 #endif
268
269 if (m_freeBlocks != 0) {
270 uint32_t next = m_nextFreeBlock;
271 GAIA_FOR(m_freeBlocks) {
272 GAIA_ASSERT(next < m_blockCnt);
273 #if GAIA_DEBUG
274 const auto bit = uint64_t(1) << next;
275 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
276 freeMask |= bit;
277 #endif
278 next = read_block_idx(next);
279 }
280
281 GAIA_ASSERT(next == InvalidBlockId);
282 }
283
284 GAIA_FOR(m_blockCnt) {
285 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * blockSize);
286 const auto& header = block_header(pMemoryBlock);
287 GAIA_ASSERT(header.m_pageAddr == (uintptr_t)this);
288 GAIA_ASSERT(((uintptr_t)pMemoryBlock % MemoryBlockAlignment) == 0);
289
290 #if GAIA_DEBUG
291 const bool isFree = (freeMask & (uint64_t(1) << i)) != 0;
292 GAIA_ASSERT((header.m_requestedBytes == 0) == isFree);
293 #endif
294 }
295
296 #if GAIA_DEBUG
297 GAIA_ASSERT((m_usedMask & freeMask) == 0);
298 const auto liveMask = m_blockCnt == 64 ? ~uint64_t(0) : ((uint64_t(1) << m_blockCnt) - 1);
299 GAIA_ASSERT((m_usedMask | freeMask) == liveMask);
300 #endif
301 #endif
302 }
303
304 #if GAIA_DEBUG
305 GAIA_NODISCARD uint64_t requested_bytes() const {
306 if (m_usedBlocks == 0)
307 return 0;
308
309 uint64_t freeMask = 0;
310 uint32_t next = m_nextFreeBlock;
311 GAIA_FOR(m_freeBlocks) {
312 GAIA_ASSERT(next < m_blockCnt);
313 const auto bit = uint64_t(1) << next;
314 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
315 freeMask |= bit;
316 next = read_block_idx(next);
317 }
318
319 uint64_t requested = 0;
320 GAIA_FOR(m_blockCnt) {
321 if ((freeMask & (uint64_t(1) << i)) != 0)
322 continue;
323
324 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * mem_block_size(m_sizeType));
325 requested += block_header(pMemoryBlock).m_requestedBytes;
326 }
327
328 return requested;
329 }
330 #endif
331
332 private:
333 static MemoryBlockHeader& block_header(void* pMemoryBlock) {
334 return *(MemoryBlockHeader*)pMemoryBlock;
335 }
336
337 static const MemoryBlockHeader& block_header(const void* pMemoryBlock) {
338 return *(const MemoryBlockHeader*)pMemoryBlock;
339 }
340 };
341
342 enum class MemoryPageState : uint8_t { Detached, Empty, Partial, Full };
343
344 struct MemoryPageContainer {
346 cnt::fwd_llist<MemoryPage> pagesEmpty;
348 cnt::fwd_llist<MemoryPage> pagesPartial;
350 cnt::fwd_llist<MemoryPage> pagesFull;
351
352 GAIA_NODISCARD bool empty() const {
353 return pagesEmpty.empty() && pagesPartial.empty() && pagesFull.empty();
354 }
355 };
356
358 class ChunkAllocatorImpl {
359 friend ::gaia::ecs::ChunkAllocator;
360
362 MemoryPageContainer m_pages[MemoryBlockSizeClasses];
363
365 bool m_isDone = false;
366
367 private:
368 ChunkAllocatorImpl() = default;
369
370 void on_delete() {
371 flush(true);
372
373 // Make sure there are no leaks
374 auto memStats = stats();
375 for (const auto& s: memStats.stats) {
376 if (s.mem_total != 0) {
377 GAIA_ASSERT2(false, "ECS leaking memory");
378 GAIA_LOG_W("ECS leaking memory!");
379 diag();
380 }
381 }
382 }
383
384 public:
385 ~ChunkAllocatorImpl() {
386 on_delete();
387 }
388
389 ChunkAllocatorImpl(ChunkAllocatorImpl&& world) = delete;
390 ChunkAllocatorImpl(const ChunkAllocatorImpl& world) = delete;
391 ChunkAllocatorImpl& operator=(ChunkAllocatorImpl&&) = delete;
392 ChunkAllocatorImpl& operator=(const ChunkAllocatorImpl&) = delete;
393
395 void* alloc(uint32_t bytesWanted) {
396 GAIA_ASSERT(bytesWanted > 0);
397 GAIA_ASSERT(bytesWanted <= MaxMemoryBlockSize);
398 if (bytesWanted == 0 || bytesWanted > MaxMemoryBlockSize)
399 return nullptr;
400
401 const auto sizeType = mem_block_size_type(bytesWanted);
402 auto& container = m_pages[sizeType];
403
404 MemoryPageState prevState = MemoryPageState::Partial;
405 auto* pPage = container.pagesPartial.first;
406 if (pPage == nullptr) {
407 prevState = MemoryPageState::Empty;
408 pPage = container.pagesEmpty.first;
409 if (pPage == nullptr) {
410 prevState = MemoryPageState::Detached;
411 pPage = alloc_page(sizeType);
412 }
413 }
414
415 // Allocate a new chunk of memory
416 #if GAIA_DEBUG
417 void* pBlock = pPage->alloc_block(bytesWanted);
418 #else
419 void* pBlock = pPage->alloc_block();
420 #endif
421
422 move_page(container, pPage, prevState, state_for(*pPage));
423 verify();
424 return pBlock;
425 }
426
427 GAIA_CLANG_WARNING_PUSH()
428 // Memory is aligned so we can silence this warning
429 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
430
432 void free(void* pBlock) {
433 GAIA_ASSERT(pBlock != nullptr);
434 if (pBlock == nullptr)
435 return;
436
437 // Decode the page from the address
438 const auto& header = *(const MemoryBlockHeader*)((uint8_t*)pBlock - MemoryBlockUsableOffset);
439 const auto pageAddr = header.m_pageAddr;
440 GAIA_ASSERT(pageAddr % sizeof(uintptr_t) == 0);
441 #if GAIA_DEBUG
442 GAIA_ASSERT(header.m_requestedBytes > 0);
443 #endif
444 auto* pPage = (MemoryPage*)pageAddr;
445 const auto prevState = state_for(*pPage);
446
447 auto& container = m_pages[pPage->m_sizeType];
448
449 #if GAIA_ASSERT_ENABLED
450 if (prevState == MemoryPageState::Full) {
451 const auto res = container.pagesFull.has(pPage);
452 GAIA_ASSERT(res && "Memory page couldn't be found among full pages");
453 } else if (prevState == MemoryPageState::Partial) {
454 const auto res = container.pagesPartial.has(pPage);
455 GAIA_ASSERT(res && "Memory page couldn't be found among partial pages");
456 } else {
457 GAIA_ASSERT(false && "Allocated block can't belong to an empty page");
458 }
459 #endif
460
461 // Free the chunk
462 pPage->free_block(pBlock);
463
464 // Update lists
465 move_page(container, pPage, prevState, state_for(*pPage));
466 verify();
467
468 // Special handling for the allocator signaled to destroy itself
469 if (m_isDone) {
470 if (pPage->empty()) {
471 container.pagesEmpty.unlink(pPage);
472 free_page(pPage);
473 }
474
475 try_delete_this();
476 }
477 }
478
479 GAIA_CLANG_WARNING_POP()
480
481
482 ChunkAllocatorStats stats() const {
483 ChunkAllocatorStats stats{};
484 for (uint32_t sizeType = 0; sizeType < MemoryBlockSizeClasses; ++sizeType)
485 stats.stats[sizeType] = page_stats(sizeType);
486 return stats;
487 }
488
491 void flush(bool releaseAll = false) {
492 uint32_t i = 0;
493 for (auto& page: m_pages)
494 flushPages(page, i++, releaseAll);
495 verify();
496 }
497
499 void diag() const {
500 auto diagPage = [](const ChunkAllocatorPageStats& stats, uint32_t sizeType) {
501 GAIA_LOG_N("ChunkAllocator %uK stats", mem_block_size(sizeType) / 1024);
502 GAIA_LOG_N(" Allocated: %" PRIu64 " B", stats.mem_total);
503 GAIA_LOG_N(" Reserved by live blocks: %" PRIu64 " B", stats.mem_used);
504 GAIA_LOG_N(" Pages: %u", stats.num_pages);
505 GAIA_LOG_N(" Reusable pages: %u", stats.num_pages_free);
506 #if !GAIA_DEBUG
507 GAIA_LOG_N(
508 " Utilization: %.1f%%",
509 stats.mem_total ? 100.0 * ((double)stats.mem_used / (double)stats.mem_total) : 0);
510 #else
511 GAIA_LOG_N(" Requested: %" PRIu64 " B", stats.mem_requested);
512 GAIA_LOG_N(" Free capacity: %" PRIu64 " B", stats.mem_total - stats.mem_used);
513 GAIA_LOG_N(" Internal slack: %" PRIu64 " B", stats.mem_used - stats.mem_requested);
514 GAIA_LOG_N(
515 " Utilization: %.1f%%",
516 stats.mem_total ? 100.0 * ((double)stats.mem_requested / (double)stats.mem_total) : 0);
517 GAIA_LOG_N(" Empty pages: %u", stats.num_pages_empty);
518 #endif
519 };
520
521 auto memStats = stats();
522 for (uint32_t sizeType = 0; sizeType < MemoryBlockSizeClasses; ++sizeType)
523 diagPage(memStats.stats[sizeType], sizeType);
524 }
525
526 void verify() const {
527 #if GAIA_ASSERT_ENABLED
528 for (uint32_t sizeType = 0; sizeType < MemoryBlockSizeClasses; ++sizeType)
529 verify_container(m_pages[sizeType], sizeType);
530 #endif
531 }
532
533 private:
534 static constexpr const char* s_strChunkAlloc_Chunk = "Chunk";
535 static constexpr const char* s_strChunkAlloc_MemPage = "MemoryPage";
536
537 static MemoryPage* alloc_page(uint8_t sizeType) {
538 const uint32_t size = mem_block_size(sizeType) * MemoryPage::NBlocks;
539 auto* pPageData = mem::AllocHelper::alloc_alig<uint8_t>(s_strChunkAlloc_Chunk, MemoryBlockAlignment, size);
540 auto* pMemoryPage = mem::AllocHelper::alloc<MemoryPage>(s_strChunkAlloc_MemPage);
541 return new (pMemoryPage) MemoryPage(pPageData, sizeType);
542 }
543
544 static void free_page(MemoryPage* pMemoryPage) {
545 GAIA_ASSERT(pMemoryPage != nullptr);
546
547 mem::AllocHelper::free_alig(s_strChunkAlloc_Chunk, pMemoryPage->m_data);
548 pMemoryPage->~MemoryPage();
549 mem::AllocHelper::free(s_strChunkAlloc_MemPage, pMemoryPage);
550 }
551
552 void done() {
553 m_isDone = true;
554 }
555
556 void try_delete_this() {
557 // When there is nothing left, delete the allocator
558 bool allEmpty = true;
559 for (const auto& c: m_pages)
560 allEmpty = allEmpty && c.empty();
561 if (allEmpty)
562 delete this;
563 }
564
565 static constexpr uint32_t warm_pages_to_keep(uint32_t sizeType) {
566 constexpr uint8_t WarmPagesPerSizeClass[] = {1, 1, 0, 0};
567 return WarmPagesPerSizeClass[sizeType];
568 }
569
570 static MemoryPageState state_for(const MemoryPage& page) {
571 if (page.empty())
572 return MemoryPageState::Empty;
573 if (page.full())
574 return MemoryPageState::Full;
575 return MemoryPageState::Partial;
576 }
577
578 static cnt::fwd_llist<MemoryPage>& page_list(MemoryPageContainer& container, MemoryPageState state) {
579 switch (state) {
580 case MemoryPageState::Empty:
581 return container.pagesEmpty;
582 case MemoryPageState::Partial:
583 return container.pagesPartial;
584 default:
585 GAIA_ASSERT(state == MemoryPageState::Full);
586 return container.pagesFull;
587 }
588 }
589
590 static void move_page(
591 MemoryPageContainer& container, MemoryPage* pPage, MemoryPageState fromState, MemoryPageState toState) {
592 if (fromState == toState)
593 return;
594
595 if (fromState != MemoryPageState::Detached)
596 page_list(container, fromState).unlink(pPage);
597 page_list(container, toState).link(pPage);
598 }
599
600 [[maybe_unused]] static void verify_page_membership(
601 [[maybe_unused]] const MemoryPageContainer& container, //
602 [[maybe_unused]] const MemoryPage& page, //
603 [[maybe_unused]] MemoryPageState expectedState //
604 ) {
605 (void)container;
606 GAIA_ASSERT(state_for(page) == expectedState);
607 GAIA_ASSERT(page.get_fwd_llist_link().linked());
608 }
609
610 static void verify_container(const MemoryPageContainer& container, uint32_t sizeType) {
611 (void)sizeType;
612 for (const auto& page: container.pagesEmpty) {
613 GAIA_ASSERT(page.m_sizeType == sizeType);
614 verify_page_membership(container, page, MemoryPageState::Empty);
615 page.verify();
616 }
617
618 for (const auto& page: container.pagesPartial) {
619 GAIA_ASSERT(page.m_sizeType == sizeType);
620 verify_page_membership(container, page, MemoryPageState::Partial);
621 page.verify();
622 }
623
624 for (const auto& page: container.pagesFull) {
625 GAIA_ASSERT(page.m_sizeType == sizeType);
626 verify_page_membership(container, page, MemoryPageState::Full);
627 page.verify();
628 }
629 }
630
631 ChunkAllocatorPageStats page_stats(uint32_t sizeType) const {
632 ChunkAllocatorPageStats stats{};
633 const auto& container = m_pages[sizeType];
634 const auto blockSize = (uint64_t)mem_block_size(sizeType);
635 const auto pageSize = blockSize * MemoryPage::NBlocks;
636
637 stats.num_pages = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size() +
638 (uint32_t)container.pagesFull.size();
639 stats.num_pages_free = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size();
640 stats.mem_total = stats.num_pages * pageSize;
641 stats.mem_used = container.pagesFull.size() * pageSize;
642
643 #if GAIA_DEBUG
644 stats.num_pages_empty = (uint32_t)container.pagesEmpty.size();
645
646 for (const auto& page: container.pagesFull)
647 stats.mem_requested += page.requested_bytes();
648
649 for (const auto& page: container.pagesPartial) {
650 stats.mem_used += page.used_blocks_cnt() * blockSize;
651 stats.mem_requested += page.requested_bytes();
652 }
653 #else
654 for (const auto& page: container.pagesPartial)
655 stats.mem_used += page.used_blocks_cnt() * blockSize;
656 #endif
657
658 return stats;
659 }
660
663 void flushPages(MemoryPageContainer& container, uint32_t sizeType, bool releaseAll) {
664 const bool keepWarmPage = !releaseAll && warm_pages_to_keep(sizeType) != 0;
665 bool keptWarmPage = false;
666 for (auto it = container.pagesEmpty.begin(); it != container.pagesEmpty.end();) {
667 auto* pPage = &(*it);
668 ++it;
669
670 // Skip non-empty pages
671 if (!pPage->empty())
672 continue;
673
674 if (keepWarmPage && !keptWarmPage) {
675 keptWarmPage = true;
676 continue;
677 }
678
679 container.pagesEmpty.unlink(pPage);
680 free_page(pPage);
681 }
682 }
683 };
684 } // namespace detail
685
686#endif
687
688 } // namespace ecs
689} // namespace gaia
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition chunk_allocator.h:19