Plasma Engine  2.0
Loading...
Searching...
No Matches
LargeBlockAllocator_inl.h
1
2template <typename T, plUInt32 SizeInBytes>
3PL_ALWAYS_INLINE plDataBlock<T, SizeInBytes>::plDataBlock(T* pData, plUInt32 uiCount)
4{
5 m_pData = pData;
6 m_uiCount = uiCount;
7}
8
9template <typename T, plUInt32 SizeInBytes>
11{
12 PL_ASSERT_DEV(m_uiCount < CAPACITY, "Block is full.");
13 return m_pData + m_uiCount++;
14}
15
16template <typename T, plUInt32 SizeInBytes>
17PL_FORCE_INLINE T* plDataBlock<T, SizeInBytes>::PopBack()
18{
19 PL_ASSERT_DEV(m_uiCount > 0, "Block is empty");
20 --m_uiCount;
21 return m_pData + m_uiCount;
22}
23
24template <typename T, plUInt32 SizeInBytes>
25PL_ALWAYS_INLINE bool plDataBlock<T, SizeInBytes>::IsEmpty() const
26{
27 return m_uiCount == 0;
28}
29
30template <typename T, plUInt32 SizeInBytes>
31PL_ALWAYS_INLINE bool plDataBlock<T, SizeInBytes>::IsFull() const
32{
33 return m_uiCount == CAPACITY;
34}
35
36template <typename T, plUInt32 SizeInBytes>
37PL_FORCE_INLINE T& plDataBlock<T, SizeInBytes>::operator[](plUInt32 uiIndex) const
38{
39 PL_ASSERT_DEV(uiIndex < m_uiCount, "Out of bounds access. Data block has {0} elements, trying to access element at index {1}.", m_uiCount, uiIndex);
40 return m_pData[uiIndex];
41}
42
44
45template <plUInt32 BlockSize>
46plLargeBlockAllocator<BlockSize>::plLargeBlockAllocator(plStringView sName, plAllocator* pParent, plAllocatorTrackingMode mode)
47 : m_TrackingMode(mode)
48 , m_SuperBlocks(pParent)
49 , m_FreeBlocks(pParent)
50{
51 static_assert(BlockSize >= 4096, "Block size must be 4096 or bigger");
52
53 m_Id = plMemoryTracker::RegisterAllocator(sName, mode, plPageAllocator::GetId());
55
56 const plUInt32 uiPageSize = plSystemInformation::Get().GetMemoryPageSize();
57 PL_IGNORE_UNUSED(uiPageSize);
58 PL_ASSERT_DEV(uiPageSize <= BlockSize, "Memory Page size is bigger than block size.");
59 PL_ASSERT_DEV(BlockSize % uiPageSize == 0, "Blocksize ({0}) must be a multiple of page size ({1})", BlockSize, uiPageSize);
60}
61
62template <plUInt32 BlockSize>
64{
65 PL_ASSERT_RELEASE(m_ThreadID == plThreadUtils::GetCurrentThreadID(), "Allocator is deleted from another thread");
66 plMemoryTracker::DeregisterAllocator(m_Id);
67
68 for (plUInt32 i = 0; i < m_SuperBlocks.GetCount(); ++i)
69 {
70 plPageAllocator::DeallocatePage(m_SuperBlocks[i].m_pBasePtr);
71 }
72}
73
74template <plUInt32 BlockSize>
75template <typename T>
77{
78 struct Helper
79 {
80 enum
81 {
83 };
84 };
85
86 static_assert(
87 Helper::BLOCK_CAPACITY >= 1, "Type is too big for block allocation. Consider using regular heap allocation instead or increase the block size.");
88
89 plDataBlock<T, BlockSize> block(static_cast<T*>(Allocate(PL_ALIGNMENT_OF(T))), 0);
90 return block;
91}
92
93template <plUInt32 BlockSize>
94template <typename T>
96{
97 Deallocate(inout_block.m_pData);
98 inout_block.m_pData = nullptr;
99 inout_block.m_uiCount = 0;
100}
101
102template <plUInt32 BlockSize>
104{
105 return plMemoryTracker::GetAllocatorName(m_Id);
106}
107
108template <plUInt32 BlockSize>
110{
111 return m_Id;
112}
113
114template <plUInt32 BlockSize>
116{
117 return plMemoryTracker::GetAllocatorStats(m_Id);
118}
119
120template <plUInt32 BlockSize>
122{
123 PL_ASSERT_RELEASE(plMath::IsPowerOf2((plUInt32)uiAlign), "Alignment must be power of two");
124
125 plTime fAllocationTime = plTime::Now();
126
127 PL_LOCK(m_Mutex);
128
129 void* ptr = nullptr;
130
131 if (!m_FreeBlocks.IsEmpty())
132 {
133 // Re-use a super block
134 plUInt32 uiFreeBlockIndex = m_FreeBlocks.PeekBack();
135 m_FreeBlocks.PopBack();
136
137 const plUInt32 uiSuperBlockIndex = uiFreeBlockIndex / SuperBlock::NUM_BLOCKS;
138 const plUInt32 uiInnerBlockIndex = uiFreeBlockIndex & (SuperBlock::NUM_BLOCKS - 1);
139 SuperBlock& superBlock = m_SuperBlocks[uiSuperBlockIndex];
140 ++superBlock.m_uiUsedBlocks;
141
142 ptr = plMemoryUtils::AddByteOffset(superBlock.m_pBasePtr, uiInnerBlockIndex * BlockSize);
143 }
144 else
145 {
146 // Allocate a new super block
147 void* pMemory = plPageAllocator::AllocatePage(SuperBlock::SIZE_IN_BYTES);
148 PL_CHECK_ALIGNMENT(pMemory, uiAlign);
149
150 SuperBlock superBlock;
151 superBlock.m_pBasePtr = pMemory;
152 superBlock.m_uiUsedBlocks = 1;
153
154 m_SuperBlocks.PushBack(superBlock);
155
156 const plUInt32 uiBlockBaseIndex = (m_SuperBlocks.GetCount() - 1) * SuperBlock::NUM_BLOCKS;
157 for (plUInt32 i = SuperBlock::NUM_BLOCKS - 1; i > 0; --i)
158 {
159 m_FreeBlocks.PushBack(uiBlockBaseIndex + i);
160 }
161
162 ptr = pMemory;
163 }
164
165 if (m_TrackingMode >= plAllocatorTrackingMode::AllocationStats)
166 {
167 plMemoryTracker::AddAllocation(m_Id, m_TrackingMode, ptr, BlockSize, uiAlign, plTime::Now() - fAllocationTime);
168 }
169
170 return ptr;
171}
172
173template <plUInt32 BlockSize>
175{
176 PL_LOCK(m_Mutex);
177
178 if (m_TrackingMode >= plAllocatorTrackingMode::AllocationStats)
179 {
180 plMemoryTracker::RemoveAllocation(m_Id, ptr);
181 }
182
183 // find super block
184 bool bFound = false;
185 plUInt32 uiSuperBlockIndex = m_SuperBlocks.GetCount();
186 std::ptrdiff_t diff = 0;
187
188 for (; uiSuperBlockIndex-- > 0;)
189 {
190 diff = (char*)ptr - (char*)m_SuperBlocks[uiSuperBlockIndex].m_pBasePtr;
191 if (diff >= 0 && diff < SuperBlock::SIZE_IN_BYTES)
192 {
193 bFound = true;
194 break;
195 }
196 }
197
198 PL_IGNORE_UNUSED(bFound);
199 PL_ASSERT_DEV(bFound, "'{0}' was not allocated with this allocator", plArgP(ptr));
200
201 SuperBlock& superBlock = m_SuperBlocks[uiSuperBlockIndex];
202 --superBlock.m_uiUsedBlocks;
203
204 if (superBlock.m_uiUsedBlocks == 0 && m_FreeBlocks.GetCount() > SuperBlock::NUM_BLOCKS * 4)
205 {
206 // give memory back
207 plPageAllocator::DeallocatePage(superBlock.m_pBasePtr);
208
209 m_SuperBlocks.RemoveAtAndSwap(uiSuperBlockIndex);
210 const plUInt32 uiLastSuperBlockIndex = m_SuperBlocks.GetCount();
211
212 // patch free list
213 for (plUInt32 i = 0; i < m_FreeBlocks.GetCount(); ++i)
214 {
215 const plUInt32 uiIndex = m_FreeBlocks[i];
216 const plUInt32 uiSBIndex = uiIndex / SuperBlock::NUM_BLOCKS;
217
218 if (uiSBIndex == uiSuperBlockIndex)
219 {
220 // points to the block we just removed
221 m_FreeBlocks.RemoveAtAndSwap(i);
222 --i;
223 }
224 else if (uiSBIndex == uiLastSuperBlockIndex)
225 {
226 // points to the block we just swapped
227 m_FreeBlocks[i] = uiSuperBlockIndex * SuperBlock::NUM_BLOCKS + (uiIndex & (SuperBlock::NUM_BLOCKS - 1));
228 }
229 }
230 }
231 else
232 {
233 // add block to free list
234 const plUInt32 uiInnerBlockIndex = (plUInt32)(diff / BlockSize);
235 m_FreeBlocks.PushBack(uiSuperBlockIndex * SuperBlock::NUM_BLOCKS + uiInnerBlockIndex);
236 }
237}
Base class for all memory allocators.
Definition Allocator.h:23
A block allocator which can only allocates blocks of memory at once.
Definition LargeBlockAllocator.h:40
static T * AddByteOffset(T *pPtr, std::ptrdiff_t offset)
Returns the address stored in ptr plus the given byte offset iOffset, cast to type T.
plStringView represent a read-only sub-string of a larger string, as it can store a dedicated string ...
Definition StringView.h:34
static const plSystemInformation & Get()
Allows access to the current system configuration.
Definition SystemInformation.h:131
plUInt32 GetMemoryPageSize() const
Returns the size of a memory page in bytes.
Definition SystemInformation.h:106
static plThreadID GetCurrentThreadID()
Returns an identifier for the currently running thread.
Definition ThreadUtils_Posix.h:42
constexpr PL_FORCE_INLINE bool IsPowerOf2(plInt32 value)
Returns true, if there exists some x with 2^x == value.
Definition Math_inl.h:260
Definition Allocator.h:26
Definition FormatStringArgs.h:76
This struct represents a block of type T, typically 4kb.
Definition LargeBlockAllocator.h:14
The time class encapsulates a double value storing the time in seconds.
Definition Time.h:12
static plTime Now()
Gets the current time.
Definition Time_Posix.h:12