ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
monoallocator.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !defined(ALIB_DOX)
10# if !defined(HPP_ALIB_MONOMEM_MONOALLOCATOR)
12# endif
13# if !defined(HPP_ALIB_MONOMEM_MONOMEM)
15# endif
16# if ALIB_ALOX && !defined (HPP_ALOX_LOGTOOLS)
17# include "alib/alox/logtools.hpp"
18# endif
19# if ALIB_STRINGS && ALIB_DEBUG_MONOMEM && !defined(HPP_ALIB_STRINGS_ASTRING)
21# endif
22
23# if ALIB_THREADS && ALIB_CAMP
26# endif
27# if ALIB_DEBUG_MONOMEM
28# if ALIB_ALOX
30# endif
32# include <atomic>
33# endif
34#endif // !defined(ALIB_DOX)
35
36
37namespace alib {
38
39/** ************************************************************************************************
40 * This \alibmod implements the concept of <em>"monotonic allocation"</em> by providing central class
41 * \alib{monomem;MonoAllocator} and a few corresponding container and utility types.
42 *
43 * Please consult \ref alib_mod_monomem "ALib Module Memory - Programmer's Manual"
44 * for further information.
45 **************************************************************************************************/
46namespace monomem {
47
48
49// #################################################################################################
50// Globals
51// #################################################################################################
54
55
56#if ALIB_THREADS && ALIB_CAMP && !defined(ALIB_DOX)
57
58 void MonoAllocator::dbgCheckGlobalAllocatorLock()
59 {
61 || !alib::BASECAMP.IsBootstrapped()
64 ,"MONOMEM", "Global MonoAllocator not acquired." )
65 }
66
67#endif
68
69#if ALIB_DEBUG_MONOMEM && !defined(ALIB_DOX)
70 bool dbgLogLock(bool lock)
71 {
72 static std::atomic_int locker;
73 if (lock)
74 {
75 if( locker++
76 || !alib::ALOX.IsBootstrapped()
77 || alib::lox::Log::DebugLogger == nullptr )
78 return false;
79 return true;
80 }
82
83 locker--;
84 return true;
85 }
86#endif
87
88#if ALIB_DEBUG
93#endif
94
95
96// #################################################################################################
97// MonoAllocator
98// #################################################################################################
100 size_t initialChunkSize, unsigned int pChunkGrowthInPercent )
101: chunk ( pfirstChunk )
102, recyclables ( nullptr )
103, nextChunksUsableSize( initialChunkSize - MaxUsableSpaceLoss() )
104, chunkGrowthInPercent( pChunkGrowthInPercent )
105{
106 #if ALIB_DEBUG_MONOMEM
108 DbgStats.HeapSize+= static_cast<size_t>( pfirstChunk->end - reinterpret_cast<char*>( pfirstChunk ) );
109 #endif
110
111
112 chunk->previous= nullptr;
113 ALIB_ASSERT_ERROR( initialChunkSize > sizeof(Chunk),
114 "MONOMEM", "The initial allocation size has to be greater than {}.", sizeof(Chunk) )
115}
116
117
118MonoAllocator::MonoAllocator( size_t initialChunkSize, unsigned int pChunkGrowthInPercent )
119: chunk ( nullptr )
120, recyclables ( nullptr )
121, nextChunksUsableSize( initialChunkSize - MaxUsableSpaceLoss() )
122, chunkGrowthInPercent( pChunkGrowthInPercent )
123{
124 DBG_MONOMEM_PATH_DOMAIN
125 DBG_MONOMEM_VERBOSE( LogDomain, "Created MonoAllocator. Initial chunk size: ", initialChunkSize )
126 ALIB_ASSERT_ERROR( initialChunkSize > sizeof(Chunk),
127 "MONOMEM", "The initial allocation size has to be greater than ", sizeof(Chunk) )
128
129}
130
131MonoAllocator* MonoAllocator::Create( size_t initialChunkSize,
132 unsigned int pChunkGrowthInPercent )
133{
134 DBG_MONOMEM_PATH_DOMAIN
135 DBG_MONOMEM_INFO( "MA/SELFCNT", "Creating self-contained MonoAllocator. Initial chunk size: ", initialChunkSize )
136
138 "MONOMEM", "The initial allocation size has to be greater than {}.", sizeof(Chunk) )
139
140 auto firstChunk= Chunk::create( initialChunkSize - MonoAllocator::MaxUsableSpaceLoss() );
141 auto allocator = reinterpret_cast<MonoAllocator*>( firstChunk->alloc( sizeof( MonoAllocator), alignof(MonoAllocator) ) );
142 return new (allocator) MonoAllocator( firstChunk, initialChunkSize, pChunkGrowthInPercent );
143}
144
146{
147 #if ALIB_DEBUG_MONOMEM
148 integer cntChunks = 0;
149
150 // need to save these values, as later, if self-contained, they are de-allocated
151 String logDomain = LogDomain;
152 integer qtyAllocations = static_cast<integer>(DbgStats.QtyAllocations);
153 #endif
154
155 // first delete recyclable chunks
156 auto* cnk= recyclables;
157 while( cnk )
158 {
159 #if ALIB_DEBUG_MONOMEM
160 ++cntChunks;
161 #endif
162
163 auto* next= cnk->previous;
164 cnk->destruct();
165 cnk= next;
166 }
167
168 // then the active ones
169 cnk= chunk;
170 while( cnk )
171 {
172 auto* next= cnk->previous;
173 if( cnk )
174 {
175 cnk->destruct();
176
177 #if ALIB_DEBUG_MONOMEM
178 ++cntChunks;
179 #endif
180 }
181 cnk= next;
182 }
183
184 DBG_MONOMEM_PATH_DOMAIN
185 DBG_MONOMEM_INFO( logDomain, "MonoAllocator destructed. #Chunks: {}, #allocations: {}",
186 cntChunks, qtyAllocations )
187
188 #if ALIB_DEBUG_MONOMEM
189 ALIB_ASSERT_WARNING( cntChunks <= 15,
190 "ALIB_DEBUG_MONOMEM Warning: More than 15 chunks allocated. "
191 "Chunksize might be increased? #Chunks: ", cntChunks )
192 #endif
193}
194
196{
197 if( !chunk )
198 {
199 ALIB_ASSERT_ERROR( snapshot.chunk == nullptr, "MONOMEM",
200 "Illegal snapshot given. Chunk allocator has no allocations, yet." )
201 return;
202 }
203
204 #if ALIB_DEBUG
205 if( snapshot.chunk == nullptr )
206 {
207 Chunk* firstChunk= chunk;
208 while( firstChunk->previous )
209 firstChunk= firstChunk->previous;
210
211 ALIB_ASSERT_ERROR( !( this >= reinterpret_cast<MonoAllocator*>(firstChunk )
212 && this < reinterpret_cast<MonoAllocator*>(firstChunk->end ) ),
213 "MONOMEM",
214 "Parameterless version of MonoAllocator::Reset() was called for a "
215 "self-contained monotonic allocator created with MonoAllocator::Create()." )
216 }
217 #endif
218
219 #if ALIB_DEBUG_MONOMEM
221 #endif
222
223 // recycle chunks until snapshot chunk or end is found
224 Chunk* it= chunk;
225 while( it != snapshot.chunk )
226 {
227 it->reset();
228 Chunk* next= it->previous;
230 recyclables= it;
231 it= next;
232 }
233
234 // snapshot chunk?
235 chunk= it;
236 if( snapshot.chunk )
237 {
239 chunk->act= snapshot.actFill;
240 }
241}
242
243#if ALIB_DEBUG_MONOMEM
244 #define DBG_ALIGNMENT_INIT(chunk) size_t qtyLeftBeforeAlloc= static_cast<size_t>(chunk->end - chunk->act);
245 #define DBG_ALIGNMENT_MEASURE(chunk) if(mem) DbgStats.AlignmentWaste+= \
246 qtyLeftBeforeAlloc \
247 - static_cast<size_t>(chunk->end - chunk->act)\
248 - size;
249#else
250 #define DBG_ALIGNMENT_INIT(...)
251 #define DBG_ALIGNMENT_MEASURE(...)
252#endif
253
254
255// #################################################################################################
256// MonoAllocator::get/Free
257// #################################################################################################
258char* MonoAllocator::getCreateChunk(size_t size, size_t alignment)
259{
260 ALIB_ASSERT( !chunk || size_t(chunk->end - chunk->act) < size )
261
262 #if ALIB_DEBUG_MONOMEM
263 if( chunk )
264 DbgStats.ChunkWaste+= static_cast<size_t>(chunk->end - chunk->act);
265 #endif
266
267
268 // special treatment if size exceeds chunk size: create an own chunk and keep using current
269 if( size >= nextChunksUsableSize )
270 {
271 Chunk* newChunk= Chunk::create( size
272 // deduct what is internally added for the worst case of max-alignment
274
275 // replace with overhead caused with given alignment
276 + sizeof(Chunk) + sizeof(Chunk) % alignment
277 );
278
279 #if ALIB_DEBUG_MONOMEM
282 DbgStats.HeapSize+= static_cast<size_t>( newChunk->end - reinterpret_cast<char*>( newChunk ) );
284 "ALIB_DEBUG_MONOMEM Warning: Allocation size matches or exceeds "
285 "chunk size. Chunksize might be increased? Requested size ",
286 static_cast<integer>(size ) )
287 #endif
288
289
290 char* mem= newChunk->alloc( size, alignment );
291 ALIB_ASSERT( mem && ( newChunk->act == newChunk->end) )
292
293 // add new chunk to used ones
294 if( chunk )
295 {
296 newChunk->previous= chunk->previous;
297 chunk->previous= newChunk;
298 }
299 else
300 {
301 // first chunk exceeded. Store chunk as actual. Will be replaced with a new one with
302 // next allocation
303 chunk= newChunk;
304 newChunk->previous= nullptr;
305 }
306 return mem;
307 }
308
309
310 // search a recycle chunk (usually the first fits)
311 Chunk** previousPointer= &recyclables;
312 Chunk* recyclable = recyclables;
313 while( recyclable )
314 { DBG_ALIGNMENT_INIT( recyclable )
315 char* mem= recyclable->alloc( size, alignment ); DBG_ALIGNMENT_MEASURE( recyclable )
316 if( mem )
317 {
318 *previousPointer= recyclable->previous;
319 recyclable->previous= chunk;
320 chunk= recyclable;
321 return mem;
322 }
323
324 // this should almost never happen (only if requesting oversized objects after a reset)
325 previousPointer= &recyclable->previous;
326 recyclable = recyclable->previous;
327 }
328
329 // create new chunk
330 Chunk* previousChunk= chunk;
333 chunk->previous = previousChunk; DBG_ALIGNMENT_INIT( chunk )
334
335 #if ALIB_DEBUG_MONOMEM
337 DbgStats.HeapSize+= static_cast<size_t>( chunk->end - reinterpret_cast<char*>( chunk ) );
338 #endif
339
340 char* mem= chunk->alloc( size, alignment ); DBG_ALIGNMENT_MEASURE( chunk )
341 return mem;
342}
343
344
345// #################################################################################################
346// DbgStatistics
347// #################################################################################################
348#if ALIB_STRINGS && ALIB_DEBUG_MONOMEM
349
351{
352 NAString result;
355 result << "MonoAllocator Usage Statistics:" << NNewLine();
356
357 result << " Allocations: " << Format( DbgStats.QtyAllocations , &nf ) << NNewLine();
358 result << " Allocation Size: " << Format( DbgStats.AllocSize , &nf ) << NNewLine();
359 result << " Non-trivial: " << Format( DbgStats.QtyAllocations- DbgStats.QtyTrivialAllocations, &nf ) << NNewLine();
360 result << " Chunks: " << Format( DbgStats.QtyChunks , &nf ) << NNewLine();
361 result << " Resets: " << Format( DbgStats.QtyResets , &nf ) << NNewLine();
362
363 result << " Avg. alloc./chunk: ";
364 if( DbgStats.QtyChunks == 0 ) { result << "N/A"; ALIB_ASSERT( DbgStats.QtyAllocations == 0) }
365 else result << Format( (DbgStats.QtyAllocations / DbgStats.QtyChunks ), &nf );
366 result << NNewLine();
367
368 result << " Allocated Heap Mem.: " << Format( DbgStats.HeapSize , &nf ) << NNewLine();
369 result << " Unused chunk bytes: " << Format( DbgStats.ChunkWaste, &nf );
370 if( DbgStats.QtyChunks ) result << " (per chunk: " << Format( (DbgStats.ChunkWaste / DbgStats.QtyChunks ), &nf ) << ')';
371 result << NNewLine();
372
373 result << " Alignment waste: " << Format( DbgStats.AlignmentWaste , &nf )
374 << " (" << Format( double(DbgStats.AlignmentWaste)/double(DbgStats.AllocSize) , &nf ) << "%)"<< NNewLine();
375 result << " Chunk size exceeds: " << Format( DbgStats.QtyChunkSizeExceeds , &nf ) << NNewLine();
376
377 int qtyRecyclables= 0;
378 size_t qtyFree = 0;
379 auto* rchunks= recyclables;
380 while( rchunks != nullptr )
381 {
382 ++qtyRecyclables;
383 qtyFree+= size_t(rchunks->end - rchunks->act);
384 rchunks= rchunks->previous;
385 }
386 result << " Free in actual: " << Format( chunk->end - chunk->act , &nf ) << NNewLine();
387 result << " Free in recyclables: " << Format( qtyFree , &nf );
388 result << " (" << qtyRecyclables << " recyclables)" << NNewLine();
389
390
391 return result;
392}
393#endif
394
395#if ALIB_DEBUG_MONOMEM && !defined(ALIB_DOX)
396namespace detail {
397 void dbgMonoMemRecyclingOutput(size_t a, size_t b, size_t c, const std::type_info& typeInfo, size_t d )
398 {
399 DBG_MONOMEM_INFO( "RECYCLING",
400 "Recycling {} objects from de-allocated memory of size {} (lost {} bytes).\n"
401 "Deallocated type: {!Q<>}{!Q[]}.",
402 a, b, c, typeInfo, d )
403 }
404}
405#endif
406
407
408
409}} // namespace [alib::monomem]
static ALIB_API detail::textlogger::TextLogger * DebugLogger
Definition log.inl:53
ALIB_API MonoAllocator(size_t initialChunkSize, unsigned int chunkGrowthInPercent=200)
ALIB_API NAString DbgDumpStats()
static ALIB_API MonoAllocator * Create(size_t initialChunkSize, unsigned int chunkGrowthInPercent=200)
ALIB_API void Reset(const Snapshot &snapshot=Snapshot())
ALIB_API char * getCreateChunk(size_t size, size_t alignment)
static constexpr size_t MaxUsableSpaceLoss()
bool IsOwnedByCurrentThread() const
lang::Safeness GetSafeness() const
#define A_CHAR(STR)
#define ALIB_IF_THREADS(...)
Definition alib.hpp:303
#define ALIB_API
Definition alib.hpp:538
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:985
#define ALIB_ASSERT(cond)
Definition alib.hpp:983
#define ALIB_DOX
Definition prepro.dox:35
#define ALIB_THREADS
Definition alib.hpp:180
#define ALIB_CAMP
Definition alib.hpp:170
@ Unsafe
Omit checks or perform unsafe operations.
ALIB_API ThreadLock GlobalAllocatorLock
ALIB_API uinteger DbgStatsStringTreeNameOverflows
ALIB_API uinteger DbgStatsStringTreeNames
MonoAllocator GlobalAllocator(8 *1024)
Definition alib.cpp:57
lang::uinteger uinteger
Type alias in namespace alib.
Definition integers.hpp:289
lang::basecamp::BaseCamp BASECAMP
Definition basecamp.cpp:136
constexpr NCString NNewLine()
Definition cstring.hpp:540
lox::ALox ALOX
monomem::MonoAllocator MonoAllocator
Type alias in namespace alib.
strings::TFormat< character > Format
Type alias in namespace alib.
threads::ThreadLock ThreadLock
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286
ALIB_FORCE_INLINE char * alloc(size_t requestedSize, size_t alignment)
Chunk * previous
the previously allocated chunk.
char * end
Pointer to the first byte behind the chunk.
char * act
Pointer to the free space in the chunk.
static ALIB_FORCE_INLINE Chunk * create(size_t size)