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