Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
MallocBinned.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
6#include "CoreTypes.h"
7#include "HAL/CriticalSection.h"
8#include "HAL/LowLevelMemStats.h"
9#include "HAL/LowLevelMemTracker.h"
10#include "HAL/MallocJemalloc.h"
11#include "HAL/MemoryBase.h"
12#include "HAL/PlatformAtomics.h"
13#include "Math/UnrealMathUtility.h"
14#include "Stats/Stats.h"
15#include "Stats/Stats2.h"
16
18
19#define MEM_TIME(st)
20
21//#define USE_LOCKFREE_DELETE
22#define USE_INTERNAL_LOCKS
23#if USE_CACHE_FREED_OS_ALLOCS
24//#define CACHE_FREED_OS_ALLOCS
25#endif
26
27#ifndef USE_OS_SMALL_BLOCK_ALLOC
28#define USE_OS_SMALL_BLOCK_ALLOC PLATFORM_IOS
29#endif //USE_OS_SMALL_BLOCK_ALLOC
30
31#ifndef USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
32#define USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS (USE_OS_SMALL_BLOCK_ALLOC && PLATFORM_IOS && 0)
33#endif //USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
34
36//# define USE_COARSE_GRAIN_LOCKS
37#endif
38
39#if defined USE_LOCKFREE_DELETE
40# define USE_INTERNAL_LOCKS
41# define USE_COARSE_GRAIN_LOCKS
42#endif
43
44#if defined CACHE_FREED_OS_ALLOCS
45 #define MAX_CACHED_OS_FREES (64)
46 #if PLATFORM_64BITS
47 #define MAX_CACHED_OS_FREES_BYTE_LIMIT (64*1024*1024)
48 #else
49 #define MAX_CACHED_OS_FREES_BYTE_LIMIT (16*1024*1024)
50 #endif
51#endif
52
53#if defined USE_INTERNAL_LOCKS && !defined USE_COARSE_GRAIN_LOCKS
54# define USE_FINE_GRAIN_LOCKS
55#endif
56
58typedef int64 BINNED_STAT_TYPE;
59#else
60typedef int32 BINNED_STAT_TYPE;
61#endif
62
63//when modifying the global allocator stats, if we are using COARSE locks, then all callsites for stat modification are covered by the allocator-wide access guard. Thus the stats can be modified directly.
64//If we are using FINE locks, then we must modify the stats through atomics as the locks are either not actually covering the stat callsites, or are locking specific table locks which is not sufficient for stats.
65#if STATS
66# ifdef USE_COARSE_GRAIN_LOCKS
67# define BINNED_STAT BINNED_STAT_TYPE
68# define BINNED_INCREMENT_STATCOUNTER(counter) (++(counter))
69# define BINNED_DECREMENT_STATCOUNTER(counter) (--(counter))
70# define BINNED_ADD_STATCOUNTER(counter, value) ((counter) += (value))
71# define BINNED_PEAK_STATCOUNTER(PeakCounter, CompareVal) ((PeakCounter) = FMath::Max((PeakCounter), (CompareVal)))
72# else
73# define BINNED_STAT volatile BINNED_STAT_TYPE
74# define BINNED_INCREMENT_STATCOUNTER(counter) (FPlatformAtomics::InterlockedIncrement(&(counter)))
75# define BINNED_DECREMENT_STATCOUNTER(counter) (FPlatformAtomics::InterlockedDecrement(&(counter)))
76# define BINNED_ADD_STATCOUNTER(counter, value) (FPlatformAtomics::InterlockedAdd(&counter, (value)))
77# define BINNED_PEAK_STATCOUNTER(PeakCounter, CompareVal) {
78 BINNED_STAT_TYPE NewCompare;
79 BINNED_STAT_TYPE NewPeak;
80 do
81 {
82 NewCompare = (PeakCounter);
83 NewPeak = FMath::Max((PeakCounter), (CompareVal));
84 }
85 while (FPlatformAtomics::InterlockedCompareExchange(&(PeakCounter), NewPeak, NewCompare) != NewCompare);
86 }
87# endif
88#else
89# define BINNED_STAT BINNED_STAT_TYPE
90# define BINNED_INCREMENT_STATCOUNTER(counter)
91# define BINNED_DECREMENT_STATCOUNTER(counter)
92# define BINNED_ADD_STATCOUNTER(counter, value)
93# define BINNED_PEAK_STATCOUNTER(PeakCounter, CompareVal)
94#endif
95
96/** Malloc binned allocator specific stats. */
97DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Os Current"), STAT_Binned_OsCurrent,STATGROUP_MemoryAllocator, );
98DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Os Peak"), STAT_Binned_OsPeak,STATGROUP_MemoryAllocator, );
99DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Waste Current"), STAT_Binned_WasteCurrent,STATGROUP_MemoryAllocator, );
100DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Waste Peak"), STAT_Binned_WastePeak,STATGROUP_MemoryAllocator, );
101DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Used Current"), STAT_Binned_UsedCurrent,STATGROUP_MemoryAllocator, );
102DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Used Peak"), STAT_Binned_UsedPeak,STATGROUP_MemoryAllocator, );
103DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Binned Current Allocs"), STAT_Binned_CurrentAllocs,STATGROUP_MemoryAllocator, );
104DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Binned Total Allocs"), STAT_Binned_TotalAllocs,STATGROUP_MemoryAllocator, );
105DECLARE_MEMORY_STAT_EXTERN(TEXT("Binned Slack Current"), STAT_Binned_SlackCurrent,STATGROUP_MemoryAllocator, );
106
108DECLARE_LLM_MEMORY_STAT_EXTERN(TEXT("Nano Malloc Pages Current"), STAT_Binned_NanoMallocPages_Current,STATGROUP_LLMPlatform, );
109DECLARE_LLM_MEMORY_STAT_EXTERN(TEXT("Nano Malloc Pages Peak"), STAT_Binned_NanoMallocPages_Peak,STATGROUP_LLMPlatform, );
110DECLARE_LLM_MEMORY_STAT_EXTERN(TEXT("Nano Malloc Pages Waste Current"), STAT_Binned_NanoMallocPages_WasteCurrent,STATGROUP_LLMPlatform, );
111DECLARE_LLM_MEMORY_STAT_EXTERN(TEXT("Nano Malloc Pages Waste Peak"),STAT_Binned_NanoMallocPages_WastePeak,STATGROUP_LLMPlatform, );
112#endif //USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
113
114
115//
116// Optimized virtual memory allocator.
117//
118class FMallocBinned : public FMalloc
119{
120 struct Private;
121
122private:
123
124 // Counts.
125 enum { POOL_COUNT = 41 };
126
127 /** Maximum allocation for the pooled allocator */
129 enum { MAX_POOLED_ALLOCATION_SIZE = 32768+1 };
130
131 // Forward declares.
132 struct FFreeMem;
133 struct FPoolInfo;
134 struct FPoolTable;
135 struct PoolHashBucket;
136
137#ifdef CACHE_FREED_OS_ALLOCS
138 /** */
139 struct FFreePageBlock
140 {
141 void* Ptr;
143
145 {
146 Ptr = nullptr;
147 ByteSize = 0;
148 }
149 };
150#endif
151
152 /** Pool table. */
154 {
155 FPoolInfo* FirstPool;
156 FPoolInfo* ExhaustedPool;
157 uint32 BlockSize;
160#endif
161#if STATS
162 /** Number of currently active pools */
164
165 /** Largest number of pools simultaneously active */
167
168 /** Number of requests currently active */
170
171 /** High watermark of requests simultaneously active */
173
174 /** Minimum request size (in bytes) */
176
177 /** Maximum request size (in bytes) */
179
180 /** Total number of requests ever */
182
183 /** Total waste from all allocs in this table */
185#endif
187 : FirstPool(nullptr)
188 , ExhaustedPool(nullptr)
189 , BlockSize(0)
190#if STATS
191 , NumActivePools(0)
192 , MaxActivePools(0)
193 , ActiveRequests(0)
195 , MinRequest(0)
196 , MaxRequest(0)
197 , TotalRequests(0)
198 , TotalWaste(0)
199#endif
200 {
201
202 }
203 };
204
206
207#ifdef USE_LOCKFREE_DELETE
208 /** We can't call the constructor to TLockFreePointerList in the BinnedMalloc constructor
209 * as it attempts to allocate memory. We push this back and initialize it later but we
210 * set aside the memory before hand
211 */
214 TArray<void*> FlushedFrees;
215 bool bFlushingFrees;
217#endif
218
220
221 // PageSize dependent constants
226 /** Shift to get the reference from the indirect tables */
230 /** Shift required to get required hash table key. */
232 /** Used to mask off the bits that have been used to lookup the indirect table */
233 uint64 PoolMask;
236
237 // Variables.
242
243 PoolHashBucket* HashBuckets;
244 PoolHashBucket* HashBucketFreeList;
245
246 uint32 PageSize;
247
248#ifdef CACHE_FREED_OS_ALLOCS
252#endif
253
254#if STATS
263 /** OsCurrent - WasteCurrent - UsedCurrent. */
265 double MemTime;
266
267#if USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
272#endif //USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
273
274#endif //STATS
275
278#endif
279
280public:
281 // FMalloc interface.
282 // InPageSize - First parameter is page size, all allocs from BinnedAllocFromOS() MUST be aligned to this size
283 // AddressLimit - Second parameter is estimate of the range of addresses expected to be returns by BinnedAllocFromOS(). Binned
284 // Malloc will adjust its internal structures to make lookups for memory allocations O(1) for this range.
285 // It is ok to go outside this range, lookups will just be a little slower
286 FMallocBinned(uint32 InPageSize, uint64 AddressLimit);
287
288 virtual void InitializeStatsMetadata() override;
289
290 virtual ~FMallocBinned();
291
292 /**
293 * Returns if the allocator is guaranteed to be thread-safe and therefore
294 * doesn't need a unnecessary thread-safety wrapper around it.
295 */
296 virtual bool IsInternallyThreadSafe() const override;
297
298 /**
299 * Malloc
300 */
301 virtual void* Malloc( SIZE_T Size, uint32 Alignment ) override;
302
303 /**
304 * Realloc
305 */
306 virtual void* Realloc( void* Ptr, SIZE_T NewSize, uint32 Alignment ) override;
307
308 /**
309 * Free
310 */
311 virtual void Free( void* Ptr ) override;
312
313 /**
314 * If possible determine the size of the memory allocated at the given address
315 *
316 * @param Original - Pointer to memory we are checking the size of
317 * @param SizeOut - If possible, this value is set to the size of the passed in pointer
318 * @return true if succeeded
319 */
320 virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override;
321
322 virtual SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override;
323
324 /**
325 * Validates the allocator's heap
326 */
327 virtual bool ValidateHeap() override;
328
329 /** Called once per frame, gathers and sets all memory allocator statistics into the corresponding stats. MUST BE THREAD SAFE. */
330 virtual void UpdateStats() override;
331
332 /** Writes allocator stats from the last update into the specified destination. */
333 virtual void GetAllocatorStats( FGenericMemoryStats& out_Stats ) override;
334
335 /**
336 * Dumps allocator stats to an output device. Subclasses should override to add additional info
337 *
338 * @param Ar [in] Output device
339 */
340 virtual void DumpAllocatorStats( class FOutputDevice& Ar ) override;
341
342 virtual const TCHAR* GetDescriptiveName() override;
343};
#define STATS
Definition Build.h:317
DECLARE_MEMORY_STAT_EXTERN(TEXT("Total Physical"), STAT_TotalPhysical, STATGROUP_MemoryPlatform,)
#define ENABLE_LOW_LEVEL_MEM_TRACKER
#define USE_OS_SMALL_BLOCK_ALLOC
#define USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS
#define USE_INTERNAL_LOCKS
DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Binned Current Allocs"), STAT_Binned_CurrentAllocs, STATGROUP_MemoryAllocator,)
#define USE_FINE_GRAIN_LOCKS
#define PLATFORM_IOS
Definition Platform.h:23
#define TEXT(x)
Definition Platform.h:1108
FWindowsCriticalSection FCriticalSection
#define PLATFORM_64BITS
virtual void * Realloc(void *Ptr, SIZE_T NewSize, uint32 Alignment) override
uint64 MaxHashBuckets
FPoolTable PagePoolTable[EXTENDED_PAGE_POOL_ALLOCATION_COUNT]
uint64 IndirectPoolBitShift
PoolHashBucket * HashBuckets
uint64 HashKeyShift
uint64 BinnedOSTableIndex
uint64 IndirectPoolBlockSize
virtual bool ValidateHeap() override
virtual bool IsInternallyThreadSafe() const override
virtual ~FMallocBinned()
virtual void InitializeStatsMetadata() override
virtual const TCHAR * GetDescriptiveName() override
uint64 BinnedSizeLimit
@ EXTENDED_PAGE_POOL_ALLOCATION_COUNT
virtual void * Malloc(SIZE_T Size, uint32 Alignment) override
FCriticalSection AccessGuard
uint64 MaxBookKeepingOverhead
uint64 TableAddressLimit
FPoolTable * MemSizeToPoolTable[MAX_POOLED_ALLOCATION_SIZE+EXTENDED_PAGE_POOL_ALLOCATION_COUNT]
FMallocBinned(uint32 InPageSize, uint64 AddressLimit)
virtual void Free(void *Ptr) override
virtual bool GetAllocationSize(void *Original, SIZE_T &SizeOut) override
uint64 MaxHashBucketBits
FPoolTable PoolTable[POOL_COUNT]
FPoolTable OsTable
uint64 PoolBitShift
virtual void UpdateStats() override
virtual void DumpAllocatorStats(class FOutputDevice &Ar) override
virtual SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override
PoolHashBucket * HashBucketFreeList
virtual void GetAllocatorStats(FGenericMemoryStats &out_Stats) override
uint64 MaxHashBucketWaste
FCriticalSection CriticalSection