ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
monoallocator.t.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This "template-definition-header" file is part of module \alib_monomem of the \aliblong.
4/// It may be used to instantiate custom versions of \alib{monomem;TPoolAllocator}, working with
5/// a different \ref alib_contmono_chaining "chained" allocator.
6/// @see Manual section \ref alib_manual_appendix_t_hpp_files about the nature of ".t.hpp"-files.
7///
8/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
9/// Published under \ref mainpage_license "Boost Software License".
10//==================================================================================================
12#if !DOXYGEN
14# if ALIB_ALOX
15# include "alib/alox/logtools.hpp"
16# endif
17# if ALIB_STRINGS && ALIB_DEBUG_MONOMEM
19# endif
20# if ALIB_THREADS && ALIB_CAMP
23# endif
24# if ALIB_DEBUG_MONOMEM && ALIB_ALOX
25# include "alib/alox/aloxcamp.hpp"
26# endif
27# if ALIB_CAMP && ALIB_DEBUG
30# endif
31
32#endif // !DOXYGEN
33
34namespace alib { namespace monomem {
35
36#if !DOXYGEN
38template<typename TAllocator>
39TMonoAllocator<TAllocator>* TMonoAllocator<TAllocator>::Create( ALIB_DBG(const char* pDbgName,)
40 TAllocator& pAllocator,
41 size_t initialBufferSizeInKB,
42 unsigned int pBufferGrowthInPercent )
43{
44 using TMA= TMonoAllocator<TAllocator>;
45 ALIB_ASSERT_ERROR( initialBufferSizeInKB, "MONOMEM", "Initial buffer of 0kb requested." )
46
47 auto size= initialBufferSizeInKB * 1024;
48 auto* mem= pAllocator.allocate( size, alignof(detail::Buffer) );
49 auto* firstBuffer= new (mem) detail::Buffer( initialBufferSizeInKB * 1024 );
50
51 auto* result = reinterpret_cast<TMA*>( firstBuffer->allocate( sizeof(TMA), alignof(TMA) ) );
52 return new (result) TMA( ALIB_DBG(pDbgName,) pAllocator, firstBuffer,
53 initialBufferSizeInKB, pBufferGrowthInPercent );
54}
55#include "alib/lang/callerinfo_methods.hpp"
56#endif
57
58template<typename TAllocator>
60{
61 #if ALIB_DEBUG_CRITICAL_SECTIONS
62 DbgCriticalSectionsPH.Destruct();
63 #endif
64
65 #if ALIB_DEBUG_MONOMEM
66 integer cntBuffers = 0;
67 #endif
68
69 // first delete recyclable buffers
70 auto* cnk= recyclables;
71 while( cnk )
72 {
73 #if ALIB_DEBUG_MONOMEM
74 ++cntBuffers;
75 #endif
76
77 auto* next= cnk->previous;
78 allocMember::GetAllocator().free( cnk, cnk->Size() );
79 cnk= next;
80 }
81
82 // then the active ones
83 cnk= buffer;
84 while( cnk )
85 {
86 auto* next= cnk->previous;
87 if( cnk )
88 {
89 allocMember::GetAllocator().free( cnk, cnk->Size() );
90
91 #if ALIB_DEBUG_MONOMEM
92 ++cntBuffers;
93 #endif
94 }
95 cnk= next;
96 }
97
98 #if ALIB_DEBUG_MONOMEM
99 ALIB_ASSERT_WARNING( cntBuffers <= 15, "MONOMEM",
100 "ALIB_DEBUG_MONOMEM Warning: More than 15 buffers allocated. "
101 "Buffer size might be increased? #Buffers: ", int(cntBuffers) )
102 #endif
103 //ALIB_DCS_RELEASE
104
105}
106
107template<typename TAllocator>
109{ ALIB_DCS_WITH( *DbgCriticalSectionsPH.Get() )
110
111 #if ALIB_DEBUG
112 // check if a (forbidden!) full reset is requested on a self-contained instance
113 if( snapshot.buffer == nullptr )
114 {
115 detail::Buffer* firstBuffer= buffer;
116 while( firstBuffer->previous )
117 firstBuffer= firstBuffer->previous;
118
119 ALIB_ASSERT_ERROR( !( this >= reinterpret_cast<TMonoAllocator*>(firstBuffer )
120 && this < reinterpret_cast<TMonoAllocator*>(firstBuffer->end ) )
121 || snapshot.actFill == reinterpret_cast<char*>(1),
122 "MONOMEM",
123 "A full MonoAllocator::Reset() was requested for a self-contained monotonic "
124 "allocator created with MonoAllocator::Create()." )
125 }
126 #endif
127
128
129 #if ALIB_DEBUG_MONOMEM
130 Statistics stats;
131 #if ALIB_DEBUG_CRITICAL_SECTIONS
132 DbgCriticalSectionsPH.Get()->Release(ALIB_CALLER);
133 #endif
134 GetStatistics(stats);
135 #if ALIB_DEBUG_CRITICAL_SECTIONS
136 DbgCriticalSectionsPH.Get()->Acquire(ALIB_CALLER);
137 #endif
138 dbgStats.QtyAllocationsInclResets += dbgStats.QtyAllocations;
139 dbgStats.QtyTrivialAllocationsInclResets+= dbgStats.QtyTrivialAllocations;
140 dbgStats.AllocSizeInclResets += (stats.AllocSize - dbgStats.AlignmentWaste);
141 ++dbgStats.QtyResets;
142 dbgStats.QtyAllocations = 0;
143 dbgStats.QtyTrivialAllocations = 0;
144 dbgStats.AlignmentWaste = 0;
145 dbgStats.QtyBufferSizeExceeds = 0;
146 #endif
147
148 // recycle buffers until snapshot buffer or end is found
149 detail::Buffer* it= buffer;
150 while( it != snapshot.buffer )
151 {
152 #if !ALIB_DEBUG_ALLOCATIONS
153 it->reset();
154 #else
155 // In debug mode, we must not reset the buffer if this is a self-contained instance and
156 // this is the first buffer, because this would overwrite the self-contained content
157 if ( it->previous != nullptr || snapshot.actFill != reinterpret_cast<char*>(1) )
158 it->reset();
159 else
161 // do a 'manual' reset of the buffer, without overwriting its contents.
162 buffer->act= reinterpret_cast<char*>( buffer + 1 );
164 #endif
165
166
167 detail::Buffer* next= it->previous;
168 if ( next == nullptr )
169 {
170 #if ALIB_DEBUG_ALLOCATIONS
171 if ( snapshot.actFill != reinterpret_cast<char*>(1) )
172 memset( buffer->act, 0xD2, size_t(buffer->end - buffer->act) );
173 #endif
174 buffer= it;
175 return;
176 }
177
178 it->previous= recyclables;
179 recyclables= it;
180 it= next;
181 }
182
183 // snapshot buffer?
184 ALIB_ASSERT( snapshot.buffer )
185 ALIB_ASSERT( snapshot.buffer == it )
186 buffer= it;
187 buffer->act= snapshot.actFill;
188
189 #if ALIB_DEBUG_ALLOCATIONS
190 memset( buffer->act, 0xD2, size_t(buffer->end - buffer->act) );
191 #endif
192}
193
194template<typename TAllocator>
196{ ALIB_DCS_WITH( *DbgCriticalSectionsPH.Get() )
197
198 // search the pointer that has no previous and clear that pointer
199 detail::Buffer** it= &buffer;
200 while( (*it)->previous )
201 it= &(*it)->previous;
202 *it= nullptr;
203
204 // note: we do not need to check the recycling list, because this first buffer will
205 // never be recycled.
206}
207
208
209//==================================================================================================
210// TMonoAllocator::get/Free
211//==================================================================================================
212template<typename TAllocator>
213char* TMonoAllocator<TAllocator>::nextBuffer(size_t size, size_t alignment)
214{
215 // we always have a buffer in place, and this method is called from the inlined allocation
216 // function when the current buffer does not fit.
217
218 // special treatment if size exceeds buffer size: create an own buffer and keep using current
219 auto nextBUS= nextBuffersUsableSize;
220 if( lang::DbgAlloc::extSize( size )
221 + detail::Buffer::firstOffset(sizeof(detail::Buffer), alignment) > nextBuffersUsableSize )
222 {
223 // adjust the next buffer size to the exceeding size
224 nextBUS= lang::DbgAlloc::extSize(size)
225 + detail::Buffer::firstOffset(sizeof(detail::Buffer), alignment)
226 + alignment;
227 #if ALIB_DEBUG_MONOMEM
228 ++dbgStats.QtyBufferSizeExceeds;
229 #endif
230 ALIB_WARNING( "MONOMEM",
231 "MonoAllocator: Allocation size exceeds the next buffers' size.\n"
232 "The allocator's buffer size should be increased.\n"
233 "Requested size: ",
234 static_cast<int>(size ) )
235 }
236
237 // search a recycle buffer (usually the first fits)
238 detail::Buffer** previousPointer= &recyclables;
239 detail::Buffer* recyclable = recyclables;
240 while( recyclable )
241 { DBG_ALIGNMENT_INIT( recyclable )
242 char* mem= recyclable->allocate( size, alignment ); DBG_ALIGNMENT_MEASURE( recyclable )
243 if( mem )
244 {
245 *previousPointer= recyclable->previous;
246 recyclable->previous= buffer;
247 buffer= recyclable;
248 return mem;
249 }
250
251 // this should almost never happen (only if requesting oversized objects after a reset)
252 previousPointer= &recyclable->previous;
253 recyclable = recyclable->previous;
254 }
255
256 // create new buffer
257 detail::Buffer* previousBuffer= buffer;
258 buffer= new (allocMember::GetAllocator().allocate(nextBUS, alignof(void*))) detail::Buffer( nextBUS );
259 if ( nextBUS == nextBuffersUsableSize)
260 nextBuffersUsableSize= (nextBuffersUsableSize * bufferGrowthInPercent) / 100;
261 buffer->previous = previousBuffer; DBG_ALIGNMENT_INIT( buffer )
262
263 char* mem= buffer->allocate( size, alignment ); DBG_ALIGNMENT_MEASURE( buffer )
264 return mem;
265}
266
267//==================================================================================================
268// Statistics
269//==================================================================================================
270template<typename TAllocator>
272{ ALIB_DCS_WITH( *DbgCriticalSectionsPH.Get() )
273
274 result= Statistics();
275 result.NextBufferSize = nextBuffersUsableSize;
276
277
278 // walk recyclable buffers
279 detail::Buffer* it= buffer;
280 result.CurrentBufferSize= buffer->Size();
281 result.CurrentBufferFree= size_t(buffer->end - buffer->act);
282
283 while( it != nullptr )
284 {
285 result.QtyBuffers++;
286 result.HeapSize+= it->Size();
287 result.AllocSize+= it->Size() - size_t(it->end - it->act) - sizeof(detail::Buffer);
288 if ( it != buffer )
289 result.BufferWaste+= size_t(it->end - it->act);
290
291 it= it->previous;
292 }
293
294 it= recyclables;
295 while( it != nullptr )
296 {
297 result.QtyRecyclables++;
298 result.HeapSizeRecycled+= it->Size();
299 it= it->previous;
300 }
301}
302
303
304#if ALIB_CAMP && ALIB_DEBUG
305template<typename TAllocator>
307{
308 Statistics stats;
309 GetStatistics(stats);
310
311 NAString result;
312 NNumberFormat nf;
313 nf.Flags |= NumberFormatFlags::WriteGroupChars;
315 result << "MonoAllocator Usage Statistics:" << NNEW_LINE;
316
317 result << " Allocation Size: " << NFormat( stats.AllocSize , &nf ) << NNEW_LINE;
318 result << " Current buffer free: " << NFormat( stats.CurrentBufferFree , &nf ) << NNEW_LINE;
319 result << " Current buffer used: " << NFormat( buffer->act - reinterpret_cast<char*>(buffer), &nf ) << NNEW_LINE;
320 result << " Current buffer size: " << NFormat( stats.CurrentBufferSize , &nf ) << NNEW_LINE;
321 result << " Next buffer size: " << NFormat( stats.NextBufferSize , &nf ) << NNEW_LINE;
322 result << " Buffers in use: " << NFormat( stats.QtyBuffers , &nf ) << NNEW_LINE;
323 result << " Buffers allocated: " << NFormat( stats.QtyBuffers + stats.QtyRecyclables , &nf ) << NNEW_LINE;
324 result << " Heap size in use: " << NFormat( stats.HeapSize , &nf ) << NNEW_LINE;
325 result << " Heap size allocated: " << NFormat( stats.HeapSize + stats.HeapSizeRecycled , &nf ) << NNEW_LINE;
326 result << " Unused buffer bytes: " << NFormat( stats.BufferWaste , &nf );
327 if( stats.QtyBuffers ) result << " (per buffer: " << NFormat( stats.QtyBuffers > 1 ? (stats.BufferWaste / (stats.QtyBuffers-1)) : 0, &nf ) << ')';
328 result << NNEW_LINE;
329
330 #if ALIB_DEBUG_MONOMEM
331 result << " Dbg: Allocations: " << NFormat( dbgStats.QtyAllocations , &nf ) << NNEW_LINE;
332 result << " Dbg: Non-trivial: " << NFormat( dbgStats.QtyAllocations- dbgStats.QtyTrivialAllocations, &nf ) << NNEW_LINE;
333 result << " Dbg: Resets: " << NFormat( dbgStats.QtyResets , &nf ) << NNEW_LINE;
334
335 result << " Dbg: #Allocs/buffer: ";
336 if( stats.QtyBuffers == 0 ) { result << "N/A"; ALIB_ASSERT( dbgStats.QtyAllocations == 0) }
337 else result << NFormat( (dbgStats.QtyAllocations / stats.QtyBuffers ), &nf );
338 result << NNEW_LINE;
339
340 result << " Dbg: Alignm. waste: " << NFormat( dbgStats.AlignmentWaste , &nf )
341 << " (" << NFormat( double(dbgStats.AlignmentWaste)/double(stats.AllocSize) *100. , &nf ) << "%)"<< NNEW_LINE;
342 result << " Dbg: Qty exceeds: " << NFormat( dbgStats.QtyBufferSizeExceeds , &nf ) << NNEW_LINE;
343 #endif // ALIB_DEBUG_MONOMEM
344
345 return result;
346}
347#endif // ALIB_STRINGS && ALIB_DEBUG
348
349}} // namespace [alib::monomem]
350
char * actFill
Pointer to the first free byte in the current buffer.
detail::Buffer * buffer
The current buffer.
ALIB_API NAString DbgDumpStatistics()
ALIB_API void GetStatistics(Statistics &result)
ALIB_API char * nextBuffer(size_t size, size_t alignment)
ALIB_API ~TMonoAllocator()
Destructor. Disposes all memory allocated with ChainedAllocator.
ALIB_API void Reset(Snapshot snapshot=Snapshot())
static ALIB_API TMonoAllocator * Create(const char *dbgName, TAllocator &pAllocator, size_t initialBufferSizeInKB, unsigned int bufferGrowthInPercent=200)
#define ALIB_WARNING(...)
Definition alib.hpp:1268
#define ALIB_CALLER
Definition alib.hpp:1164
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
#define ALIB_DCS_WITH(CS)
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:1272
#define ALIB_DBG(...)
Definition alib.hpp:390
#define ALIB_ASSERT(cond)
Definition alib.hpp:1270
Definition alib.cpp:69
strings::TFormat< nchar > NFormat
Type alias in namespace alib.
constexpr NCString NNEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.hpp:589
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
static constexpr size_t extSize(TIntegral size)
unsigned int QtyBuffers
The number of created buffers.
size_t CurrentBufferFree
The free space in the current buffer.
size_t HeapSizeRecycled
The number of bytes allocated at the heap.
size_t CurrentBufferSize
The size of the current buffer.
unsigned int QtyRecyclables
The number of created buffers.
size_t NextBufferSize
The planned size of the next buffer (that is not an oversize-allocation).
static constexpr size_t firstOffset(size_t firstObject, size_t alignment)
char * end
Pointer to the first byte behind the buffer.
ALIB_FORCE_INLINE char * allocate(size_t size, size_t alignment)
char * act
Pointer to the next free space in the buffer.
Buffer * previous
the previously allocated buffer.
NumberFormatFlags Flags
The flag field.