ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
rttrallocator.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of the \aliblong. It does not belong to an \alibmod and is
4/// included in any \alibdist.
5///
6/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
7/// Published under \ref mainpage_license "Boost Software License".
8//==================================================================================================
9#ifndef HPP_ALIB_LANG_RTTRALLOCATOR
10#define HPP_ALIB_LANG_RTTRALLOCATOR
11#pragma once
12
13#if ALIB_DEBUG
15#endif
18
19#if !DOXYGEN
20# if ALIB_DEBUG
21# include <iostream>
22# define DBGMEMOUT(output) std::cout << output << std::endl;
23# else
24# define DBGMEMOUT(output)
25# endif
26#endif //if DOXYGEN
27
28#include <memory>
29
30
31#if ALIB_MONOMEM
32namespace alib::monomem
33{
34 template<typename TAllocator>class TMonoAllocator;
35}
36#endif
37
38namespace alib::lang {
39
40//==================================================================================================
41/// This structs' name stands for "run-time type recycling allocator".
42/// The struct encapsulates an allocator and recycles (caches) objects whose size and alignment are
43/// only determined and detected at run-time.
44/// This type should \e only be used in combination with struct
45/// \alib{lang;StdContainerAllocatorRecycling} or in similar (unlikely) situations when the type of
46/// recyclable objects is unknown at compile time.
47/// \note The reason why the size of node types of containers of the C++ standard library is known
48/// only at run-time, is that the implementation of the containers is not standardized.
49/// Hence, in fact the node size is of course known at compile time, it is just not accessible
50/// in a platform-independent fashion.
51///
52/// This type is provided by \alib to support recycling of monotonically allocated memory with
53/// container types provided by the C++ standard library. With those, the internal node types are unspecified.
54/// A typical implementation and use of the containers will always allocate the same object size.
55/// This is detected with the first allocation and from this point in time, future de-allocations
56/// will recycle pieces of memory of exactly this type.
57///
58/// If type \alib{lang::HeapAllocator} is given as template parameter \p{TAllocator}, this recycler
59/// uses <c>std::malloc</c> and <c>std::free</c> for memory allocation and de-allocation.
60/// While such operation mode does not imply all performance benefits of monotonic allocation
61/// scenarios, still the recycling of node objects may avoid many malloc/free operations and
62/// therefore reduce memory fragmentation significantly.<br>
63/// Method #RecycleChunk will not create recyclable objects in that mode, but will duly
64/// use <c>std::free</c> to directly free a chunk of non-object size.
65///
66/// @tparam TAllocator The allocator type, as prototyped with \alib{lang;Allocator}.
67//==================================================================================================
68template<typename TAllocator>
69struct RTTRAllocator : AllocatorMember<TAllocator>
70{
71 /// The allocator type that \p{TAllocator} specifies.
73
74 /// The node type of the internal node type used for stacking recyclables. Besides inheriting
75 /// the single-list pointer, this type is empty.
76 struct Node : SidiNodeBase<Node>
77 {};
78
79 /// List of destructed objects available for recycling.
81
82 /// The object size of recyclables. Will be detected with the first invocation of #Get.
84
85 /// The required object alignment. Will be detected with the first invocation of #Get.
87
88 #if ALIB_DEBUG
89 /// The detected object's run-time type information struct.<br>
90 /// Available only in debug-builds.
91 const std::type_info* dbgDetectedObjectTypeInfo = nullptr;
92
93 /// Flag on raising an \alib warning. Defaults to \c true and set to \c false when a
94 /// corresponding warning was given..<br>
95 /// Available only in debug-builds.
97
98 /// Flag on raising an \alib warning. Defaults to \c true and set to \c false when a
99 /// corresponding warning was given..<br>
100 /// Available only in debug-builds.
102
103 /// Flag on raising an \alib warning. Defaults to \c true and set to \c false when a
104 /// corresponding warning was given..<br>
105 /// Available only in debug-builds.
107
108 /// Flag on raising an \alib warning. Defaults to \c true and set to \c false when a
109 /// corresponding warning was given.<br>
110 /// Available only in debug-builds.
112 #endif
113
114 /// Constructor taking the monotonic allocator.
115 ///
116 /// If \c nullptr is given, this recycler uses <c>std::malloc</c> and <c>std::free</c> for memory
117 /// allocation and de-allocation.
118 ///
119 /// @param pAllocator The monotonic allocator. If \c nullptr is given, still recycling of node
120 /// objects is performed.
121 RTTRAllocator( TAllocator& pAllocator ) noexcept : allocBase(pAllocator) {}
122
123 // #############################################################################################
124 // ### Allocation
125 // #############################################################################################
126#if DOXYGEN
127 //==============================================================================================
128 /// Allocates or recycles memory for the dedicated object type. With the first invocation
129 /// of this method, this type is determined object type.
130 /// In debug-builds, this method will raise an \alib warning in case a different
131 /// object type is requested.
132 ///
133 /// @param size The requested size.
134 /// @param alignment The requested alignment
135 /// @param dbgTypeInfo The type information of the object to allocate.
136 /// Available only in debug-builds.
137 /// @return The requested memory.
138 //==============================================================================================
139 inline
140 void* Get( size_t size, size_t alignment, const type_info& dbgTypeInfo);
141#else
142 void* Get( size_t size, size_t alignment ALIB_DBG(, const std::type_info& dbgTypeInfo) )
143 {
144 ALIB_DBG( (void) dbgTypeInfo; )
145
146 // detect object size
147 if(detectedObjectSize == 0)
148 {
149 detectedObjectSize = size;
150 detectedObjectAlignment = alignment;
151 ALIB_DBG( dbgDetectedObjectTypeInfo = &dbgTypeInfo; )
152
153 DBGMEMOUT("Object type detected : " << DbgTypeDemangler(dbgTypeInfo).Get() )
154 ALIB_ASSERT_ERROR( alignment >= alignof(Node),
155 "MONOMEM/RTTRA", "Struct RTTRAllocator cannot be used to recycle types with an alignment "
156 "smaller than ", alignof(Node) )
157 }
158
159 if( (size == detectedObjectSize) && (detectedObjectAlignment== alignment) )
160 {
161 if( !stack.isEmpty() )
162 {
163 #if ALIB_DEBUG_MONOMEM
164 DBGMEMOUT( "Recycling object. Type: " << DbgTypeDemangler(dbgTypeInfo).Get() )
165 #endif
166 return reinterpret_cast<char*>( stack.popFront() );
167 }
168 }
169 #if ALIB_DEBUG
170 else
171 {
173 {
174 DBGMEMOUT( "Warning: ALIB/RTTRA" )
175 DBGMEMOUT("A different object was requested for allocation!")
176 DBGMEMOUT(" Previous type : <" << DbgTypeDemangler(*dbgDetectedObjectTypeInfo).Get() << ">")
177 DBGMEMOUT(" Requested type: <" << DbgTypeDemangler( dbgTypeInfo ).Get() << ">")
178 DBGMEMOUT("Note: This allocator may not be efficient when used." )
179 DBGMEMOUT(" If this is a use case using a 'std' library container, this message indicates" )
180 DBGMEMOUT(" that a RTTRAllocator was shared between different container instantiations." )
181 DBGMEMOUT(" If this is not the case, than an 'unusual' implementation of such C++ library may")
182 DBGMEMOUT(" prevent this concept from working. See ALib manual for further information" )
184 }
185 DBGMEMOUT( "Allocating a different object type \"" << DbgTypeDemangler(dbgTypeInfo).Get() << "\"")
186 DBGMEMOUT( " Note: This object cannot be recycled." )
187 return allocBase::AI().Alloc( size, alignment );
188 }
189 #endif
190
191 #if ALIB_DEBUG_MONOMEM
192 DBGMEMOUT( "Allocating object. Type: \"" << DbgTypeDemangler(dbgTypeInfo).Get() << "\"" )
193 #endif
194
195 return allocBase::AI().Alloc( size, alignment );
196 }
197#endif // DOXYGEN
198
199#if DOXYGEN
200 //==============================================================================================
201 /// Allocates memory for a type different to the dedicated, detected object type.
202 /// @param size The requested size.
203 /// @param alignment The requested alignment
204 /// @param dbgTypeInfo The type information of the object to allocate.
205 /// Available only in debug-builds.
206 /// @return The requested memory.
207 //==============================================================================================
208 inline
209 void* AllocUnrelated( size_t size, size_t alignment, const type_info& dbgTypeInfo);
210#else
211 void* AllocUnrelated( size_t size, size_t alignment ALIB_DBG(, const std::type_info& dbgTypeInfo))
212 {
213 ALIB_DBG( (void) dbgTypeInfo; )
215 DBGMEMOUT( "Allocating other. Type: " << DbgTypeDemangler(dbgTypeInfo).Get() )
216 #endif
217 return allocBase::AI().Alloc( size, alignment );
218 }
219#endif // DOXYGEN
220
221
222 // #############################################################################################
223 // ### De-allocation
224 // #############################################################################################
225#if DOXYGEN
226 //==============================================================================================
227 /// Deallocates memory for the dedicated, detected object type.
228 ///
229 /// In debug-builds, this method will raise an \alib warning in case a different
230 /// object type is deallocated as had been detected.
231 /// Furthermore, a warning is raised in case no previous call to #Get has been performed.
232 ///
233 /// @param mem The object to deallocate.
234 /// @param size The size of the object to deallocate.
235 /// @param alignment The alignment of the object to deallocate.
236 /// @param dbgTypeInfo The type information of the object to de-allocate.
237 /// Available only in debug-builds.
238 //==============================================================================================
239 inline
240 void Recycle( void* mem, size_t size, size_t alignment, const type_info& dbgTypeInfo );
241#else
242 void Recycle( void* mem, size_t size, size_t alignment ALIB_DBG(, const std::type_info& dbgTypeInfo) )
243 {
244 if( size == detectedObjectSize
245 && alignment == detectedObjectAlignment )
246 {
247 stack.pushFront( reinterpret_cast<Node*>( mem ) );
248 #if ALIB_DEBUG_MONOMEM
249 DBGMEMOUT( "Stacking object. Type: " << DbgTypeDemangler(dbgTypeInfo).Get() )
250 #endif
251 }
252 else
253 {
254 allocBase::GetAllocator().free(mem, size);
255 #if ALIB_DEBUG
256 if( detectedObjectSize == 0 )
257 {
259 {
260 DBGMEMOUT( "Warning: ALIB/RTTRA" )
261 DBGMEMOUT( "De-allocation before a first object allocation needed to detect recyclable type!" )
262 DBGMEMOUT( " De-allocated object type: <" << DbgTypeDemangler( dbgTypeInfo ).Get() << ">" )
263 DBGMEMOUT( "Note: This allocator may not be efficient when used." )
264 DBGMEMOUT( " If this is a use case using a 'std' library container, this message indicates" )
265 DBGMEMOUT( " an 'unusual' implementation of such C++ standard library." )
266 }
268 }
269 else
270 {
272 {
273 DBGMEMOUT( "Warning: ALIB/RTTRA" )
274 DBGMEMOUT( "A different object for was requested for de-allocoation!" )
275 DBGMEMOUT( " Previous type : <" << DbgTypeDemangler(*dbgDetectedObjectTypeInfo).Get() << ">" )
276 DBGMEMOUT( " Requested type: <" << DbgTypeDemangler( dbgTypeInfo ).Get() << ">" )
277 DBGMEMOUT( "Note: This allocator may not be efficient when used." )
278 DBGMEMOUT( " If this is a use case using a 'std' library container, this message indicates" )
279 DBGMEMOUT( " that a RTTRAllocator was shared between different container instantiations." )
280 DBGMEMOUT( " If this is not the case, than an 'unusual' implementation of such C++ library may")
281 DBGMEMOUT( " prevent this concept from working. See ALib manual for further information" )
283 }
284 }
285 #endif
286 }
287 }
288#endif
289
290
291#if DOXYGEN
292 //==============================================================================================
293 /// Deallocates memory for a type different to the dedicated, detected object type.
294 ///
295 /// In debug-builds, this method will raise an \alib warning no previous call to #Get
296 /// has been performed.<br>
297 /// Furthermore, a warning is raised in case that the provided memory chunk is too small
298 /// to be sliced into at least one recyclable object.
299 ///
300 /// @param mem The object to deallocate.
301 /// @param size The size of the object to deallocate.
302 /// @param dbgTypeInfo The type information of the object to de-allocate.
303 /// Available only in debug-builds.
304 //==============================================================================================
305 inline
306 void RecycleChunk( void* mem, size_t size, const type_info& dbgTypeInfo );
307#else
308 void RecycleChunk( void* memUnaligned, size_t size ALIB_DBG(, const std::type_info& dbgTypeInfo) )
309 {
310 // This whole exercise must only be done with MonoAllocator and if ALIB_DEBUG_ALLOCATIONS is
311 // not set. (This is ensured by 'TAllocator::allowsMemSplit()')
312 if( !TAllocator::allowsMemSplit() )
313 {
314 allocBase::GetAllocator().free(memUnaligned, size);
315 return;
316 }
317
318 // if object size not detected, yet, we cannot create recyclables.
319 ALIB_DBG( size_t origSize= size; )
320 if( detectedObjectSize == 0)
321 {
322 #if ALIB_DEBUG
324 {
325 DBGMEMOUT( "Warning: ALIB/RTTRA" )
326 DBGMEMOUT( "De-allocation before a first object allocation needed to detect recyclable type!" )
327 DBGMEMOUT( " De-allocated object type: <" << DbgTypeDemangler( dbgTypeInfo ).Get() << ">." )
328 DBGMEMOUT( "Note: If this recycler is used with a 'std' library container, this either" )
329 DBGMEMOUT( " indicates an 'unusual' implementation of such C++ standard library," )
330 DBGMEMOUT( " or a manual shrink of the capacity without any prior object insertion." )
332 }
333 #endif
334
335 return;
336 }
337
338 // align beginning of buffer
339 char* mem = reinterpret_cast<char*>( (size_t(memUnaligned) + detectedObjectAlignment - 1) & ~(detectedObjectAlignment -1) );
340 size-= size_t(mem - reinterpret_cast<char*>(memUnaligned));
341
342 // create recyclables
343 ALIB_DBG( size_t cntStackedObjects= 0; )
344 while(size > detectedObjectSize)
345 {
346 stack.pushFront( reinterpret_cast<Node*>( mem ) );
348 mem = reinterpret_cast<char*>(mem) + detectedObjectSize;
350 size -= detectedObjectSize;
351 ALIB_DBG( ++cntStackedObjects; )
352 }
353
354 #if ALIB_DEBUG
355 if( cntStackedObjects > 0 )
356 {
357 DBGMEMOUT( "Warning: ALIB/RTTRA" )
358 DBGMEMOUT( "De-allocated chunk's size is smaller than detected object size." )
359 DBGMEMOUT( " Deallocated object: Type: <" << DbgTypeDemangler( dbgTypeInfo ).Get() << ">" )
360 DBGMEMOUT( " Size: " << origSize << " bytes" )
361 DBGMEMOUT( " Detected object: Type: <" << DbgTypeDemangler( *dbgDetectedObjectTypeInfo ).Get() << ">" )
362 DBGMEMOUT( " Size: " << detectedObjectSize << " bytes, alignment: " << detectedObjectAlignment )
363 DBGMEMOUT( "Note: If this recycler is used with a <std::unordered_map> or <std::unordered_set>," )
364 DBGMEMOUT( " this message may be eliminated by reserving a reasonable initial bucket size." )
365 }
366
367 #endif
368 #if ALIB_DEBUG_MONOMEM
369 DBGMEMOUT( "ALIB/MEMORY" )
370 DBGMEMOUT( "Stacking " << cntStackedObjects << " objects from de-allocated memory of size "
371 << origSize << " (lost " << origSize - cntStackedObjects * detectedObjectSize
372 << " bytes). Deallocated type: " << DbgTypeDemangler(dbgTypeInfo).Get() )
373 #endif
374 }
375#endif
376
377}; // RTTRAllocator
378
379} // namespace [alib::lang]
380
381#undef DBGMEMOUT
382#endif // HPP_ALIB_LANG_RTTRALLOCATOR
383
#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_DBG(...)
Definition alib.hpp:390
#define ALIB_DEBUG_MONOMEM
Definition prepro.md:45
@ Get
Denotes to search data.
AllocatorInterface< TAllocator > AI() const noexcept
TAllocator & GetAllocator() const noexcept
TAllocator & allocator
A reference to the allocator.
const std::type_info * dbgDetectedObjectTypeInfo
AllocatorMember< TAllocator > allocBase
The allocator type that TAllocator specifies.
SidiListHook< Node > stack
List of destructed objects available for recycling.
RTTRAllocator(TAllocator &pAllocator) noexcept
size_t detectedObjectAlignment
The required object alignment. Will be detected with the first invocation of Get.
void * AllocUnrelated(size_t size, size_t alignment, const type_info &dbgTypeInfo)
void Recycle(void *mem, size_t size, size_t alignment, const type_info &dbgTypeInfo)
void * Get(size_t size, size_t alignment, const type_info &dbgTypeInfo)
size_t detectedObjectSize
The object size of recyclables. Will be detected with the first invocation of Get.
void RecycleChunk(void *mem, size_t size, const type_info &dbgTypeInfo)