6#include "HAL/Allocators/CachedOSPageAllocator.h"
7#include "HAL/Allocators/CachedOSVeryLargePageAllocator.h"
8#include "HAL/Allocators/PooledVirtualMemoryAllocator.h"
9#include "HAL/CriticalSection.h"
10#include "HAL/LowLevelMemTracker.h"
11#include "HAL/MemoryBase.h"
12#include "HAL/PlatformMath.h"
13#include "HAL/PlatformTLS.h"
14#include "HAL/UnrealMemory.h"
15#include "Math/NumericLimits.h"
16#include "Misc/AssertionMacros.h"
18#include "Templates/AlignmentTemplates.h"
19#include "Templates/Atomic.h"
23#define BINNED2_MAX_CACHED_OS_FREES (64
)
25 #define BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT (64
*1024
*1024
)
27 #define BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT (16
*1024
*1024
)
30#define BINNED2_LARGE_ALLOC 65536
31#define BINNED2_MINIMUM_ALIGNMENT_SHIFT 4
32#define BINNED2_MINIMUM_ALIGNMENT 16
33#define BINNED2_MAX_SMALL_POOL_SIZE (32768
-16
)
34#define BINNED2_SMALL_POOL_COUNT 45
37#define DEFAULT_GMallocBinned2PerThreadCaches 1
38#define DEFAULT_GMallocBinned2LockFreeCaches 0
39#define DEFAULT_GMallocBinned2BundleCount 64
40#define DEFAULT_GMallocBinned2AllocExtra 32
41#define BINNED2_MAX_GMallocBinned2MaxBundlesBeforeRecycle 8
42#define DEFAULT_GMallocBinned2MoveOSFreesOffTimeCriticalThreads 1
45 #error "AGGRESSIVE_MEMORY_SAVING must be defined"
48 #define DEFAULT_GMallocBinned2BundleSize 8192
56#ifndef BINNED2_BOOKKEEPING_AT_THE_END_OF_LARGEBLOCK
57#define BINNED2_BOOKKEEPING_AT_THE_END_OF_LARGEBLOCK 0
61#ifndef BINNED2_FORK_SUPPORT
66#define BINNED2_ALLOW_RUNTIME_TWEAKING 0
68 extern int32 GMallocBinned2PerThreadCaches;
69 extern int32 GMallocBinned2BundleSize = DEFAULT_GMallocBinned2BundleSize;
70 extern int32 GMallocBinned2BundleCount = DEFAULT_GMallocBinned2BundleCount;
71 extern int32 GMallocBinned2MaxBundlesBeforeRecycle = BINNED2_MAX_GMallocBinned2MaxBundlesBeforeRecycle;
72 extern int32 GMallocBinned2AllocExtra = DEFAULT_GMallocBinned2AllocExtra;
73 extern int32 GMallocBinned2MoveOSFreesOffTimeCriticalThreads = DEFAULT_GMallocBinned2MoveOSFreesOffTimeCriticalThreads;
85#ifndef BINNED2_ALLOCATOR_STATS
87 #define BINNED2_ALLOCATOR_STATS 0
89 #define BINNED2_ALLOCATOR_STATS 1
99extern TAtomic<int64> AllocatedSmallPoolMemory;
100extern TAtomic<int64> AllocatedOSSmallPoolMemory;
101extern TAtomic<int64> AllocatedLargePoolMemory;
102extern TAtomic<int64> AllocatedLargePoolMemoryWAlignment;
105#include "Misc/ScopeLock.h"
107extern int64 AllocatedSmallPoolMemoryValidation;
108extern FCriticalSection ValidationCriticalSection;
109extern int32 RecursionCounter;
134 struct PoolHashBucket;
152 check(NumFreeBlocks * InBlockSize +
sizeof(FFreeBlock) <= InPageSize);
170 if (IsAligned(((uintptr_t)
this)+
sizeof(FFreeBlock), BINNED2_LARGE_ALLOC))
173 uintptr_t ptr = AlignDown((uintptr_t)
this, BINNED2_LARGE_ALLOC);
175 return (uint8*) ptr + (NumFreeBlocks * BlockSize);
235 Init(InPageSize
, InNumPoolsPerPage
, AddressLimit
);
238 void Init(uint32 InPageSize, uint64 InNumPoolsPerPage, uint64 AddressLimit)
240 uint64 PoolPageToPoolBitShift =
FPlatformMath::CeilLogTwo64(InNumPoolsPerPage);
244 PoolMask = (1ull << PoolPageToPoolBitShift) - 1;
251 OutBucketIndex = uint32(OutBucketCollision & (
MaxHashBuckets - 1));
304 return !CachedOSPageAllocator.IsPartOf(Ptr) && IsAligned(Ptr, BINNED2_LARGE_ALLOC);
465 return (FFreeBlock*) (AlignDown((uintptr_t) Ptr, BINNED2_LARGE_ALLOC) + BINNED2_LARGE_ALLOC -
sizeof(FFreeBlock));
481 FScopeLock Lock(&ValidationCriticalSection);
483 void *Result = MallocInline(Size, Alignment);
484 if ( !IsOSAllocation(Result))
487 ensure( GetAllocationSize(Result, OutSize) );
488 AllocatedSmallPoolMemoryValidation += OutSize;
489 if (RecursionCounter==1)
491 check(GetTotalAllocatedSmallPoolMemory() == AllocatedSmallPoolMemoryValidation);
492 if (GetTotalAllocatedSmallPoolMemory() != AllocatedSmallPoolMemoryValidation)
509 if (Alignment > BINNED2_MINIMUM_ALIGNMENT && (Size <= BINNED2_MAX_SMALL_POOL_SIZE))
511 Size = Align(Size, Alignment);
514 void* Result =
nullptr;
518 if ((Size <= BINNED2_MAX_SMALL_POOL_SIZE))
532 Lists->AllocatedMemory += BlockSize;
537 if (Result ==
nullptr)
547 if (Alignment > BINNED2_MINIMUM_ALIGNMENT)
549 Size = Align(Size, Alignment);
551 bool bResult = (Size <= BINNED2_MAX_SMALL_POOL_SIZE);
575 bool bOldIsOsAllocation = IsOSAllocation(Ptr);
577 if (!bOldIsOsAllocation)
579 ensure(GetAllocationSize(Ptr, OldSize));
581 FScopeLock Lock(&ValidationCriticalSection);
583 void *Result = ReallocInline(Ptr, NewSize, Alignment);
584 if ( !bOldIsOsAllocation )
586 AllocatedSmallPoolMemoryValidation -= OldSize;
588 if (!IsOSAllocation(Result))
591 ensure(GetAllocationSize(Result, OutSize));
592 AllocatedSmallPoolMemoryValidation += OutSize;
594 if (RecursionCounter == 1)
596 check(GetTotalAllocatedSmallPoolMemory() == AllocatedSmallPoolMemoryValidation);
597 if (GetTotalAllocatedSmallPoolMemory() != AllocatedSmallPoolMemoryValidation)
611 if (Alignment > BINNED2_MINIMUM_ALIGNMENT && (NewSize <= BINNED2_MAX_SMALL_POOL_SIZE))
613 NewSize = Align(NewSize, Alignment);
615 if (NewSize <= BINNED2_MAX_SMALL_POOL_SIZE)
623 uint32 BlockSize = 0;
624 uint32 PoolIndex = 0;
626 bool bCanFree =
true;
635 if (NewSize && bCanFree && NewSize <= BlockSize && (PoolIndex == 0 || NewSize >
PoolIndexToBlockSize(PoolIndex - 1
)))
639 bCanFree = bCanFree && Lists
->CanFree(PoolIndex
, BlockSize
);
645 void* Result = NewSize ? Lists
->Malloc(NewPoolIndex
) :
nullptr;
649 Lists->AllocatedMemory += NewBlockSize;
652 if (Result || !NewSize)
656 FMemory::Memcpy(Result, Ptr, FPlatformMath::Min<SIZE_T>(NewSize, BlockSize));
660 bool bDidPush = Lists
->Free(Ptr
, PoolIndex
, BlockSize
);
663 Lists->AllocatedMemory -= BlockSize;
679 FScopeLock Lock(&ValidationCriticalSection);
681 if (!IsOSAllocation(Ptr))
684 ensure(GetAllocationSize(Ptr, OutSize));
685 AllocatedSmallPoolMemoryValidation -= OutSize;
688 if (RecursionCounter == 1)
690 check(GetTotalAllocatedSmallPoolMemory() == AllocatedSmallPoolMemoryValidation);
691 if (GetTotalAllocatedSmallPoolMemory() != AllocatedSmallPoolMemoryValidation)
715 Lists->AllocatedMemory -= BasePtr->BlockSize;
729 if (Free->CanaryAndForkState == CurrentCanary || Free->CanaryAndForkState == OldCanary)
744 checkSlow((Alignment & (Alignment - 1)) == 0);
752 Alignment = FPlatformMath::Max<uint32>(Alignment, OsAllocationGranularity);
754 SizeOut = Align(Count, Alignment);
756 check(SizeOut >= Count);
761 virtual void Trim(
bool bTrimThreadCaches)
override;
810#define BINNED2_INLINE (1
)
813 #define FMEMORY_INLINE_FUNCTION_DECORATOR FORCEINLINE
814 #define FMEMORY_INLINE_GMalloc (FMallocBinned2::MallocBinned2)
815 #include "FMemory.inl"
#define UE_BUILD_SHIPPING
#define AGGRESSIVE_MEMORY_SAVING
#define DEFAULT_SERVER_FAKE_FORKS
#define BINNED2_MAX_GMallocBinned2MaxBundlesBeforeRecycle
#define BINNED2_BOOKKEEPING_AT_THE_END_OF_LARGEBLOCK
#define DEFAULT_GMallocBinned2BundleCount
#define BINNED2_ALLOCATOR_STATS_VALIDATION
#define BINNED2_SMALL_POOL_COUNT
#define BINNED2_MINIMUM_ALIGNMENT_SHIFT
#define GMallocBinned2PerThreadCaches
#define DEFAULT_GMallocBinned2PerThreadCaches
#define BINNED2_LARGE_ALLOC
#define DEFAULT_GMallocBinned2MoveOSFreesOffTimeCriticalThreads
#define DEFAULT_GMallocBinned2BundleSize
#define BINNED2_ALLOW_RUNTIME_TWEAKING
#define DEFAULT_GMallocBinned2AllocExtra
#define BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT
#define BINNED2_MAX_SMALL_POOL_SIZE
#define GMallocBinned2BundleSize
#define GMallocBinned2BundleCount
#define BINNED2_ALLOCATOR_STATS
#define BINNED2_MAX_CACHED_OS_FREES
#define BINNED2_FORK_SUPPORT
#define BINNED2_MINIMUM_ALIGNMENT
#define FORCE_ANSI_ALLOCATOR
#define UE_USE_VERYLARGEPAGEALLOCATOR
FWindowsCriticalSection FCriticalSection
virtual ~FMallocBinned2()
virtual FORCEINLINE void * Malloc(SIZE_T Size, uint32 Alignment) override
static uint8 MemSizeToIndex[1+(BINNED2_MAX_SMALL_POOL_SIZE > > BINNED2_MINIMUM_ALIGNMENT_SHIFT)]
void CanaryTest(const FFreeBlock *Block) const
FORCEINLINE bool IsOSAllocation(const void *Ptr)
void FreeExternal(void *Ptr)
virtual FORCEINLINE bool GetAllocationSize(void *Ptr, SIZE_T &SizeOut) override
virtual void UpdateStats() override
void * MallocExternalLarge(SIZE_T Size, uint32 Alignment)
void * MallocExternalSmall(SIZE_T Size, uint32 Alignment)
FORCEINLINE void * MallocSelect(SIZE_T Size, uint32 Alignment)
virtual void DumpAllocatorStats(class FOutputDevice &Ar) override
FORCEINLINE void * MallocInline(SIZE_T Size, uint32 Alignment)
static uint16 SmallBlockSizesReversed[BINNED2_SMALL_POOL_COUNT]
virtual void OnPreFork() override
virtual void GetAllocatorStats(FGenericMemoryStats &out_Stats) override
TCachedOSPageAllocator< BINNED2_MAX_CACHED_OS_FREES, BINNED2_MAX_CACHED_OS_FREES_BYTE_LIMIT > CachedOSPageAllocator
virtual FORCEINLINE void Free(void *Ptr) override
void CanaryFail(const FFreeBlock *Block) const
FORCEINLINE uint32 PoolIndexToBlockSize(uint32 PoolIndex)
FPtrToPoolMapping PtrToPoolMapping
virtual bool ValidateHeap() override
virtual FORCEINLINE SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override
virtual const TCHAR * GetDescriptiveName() override
static constexpr EBlockCanary CurrentCanary
PoolHashBucket * HashBuckets
static uint32 OsAllocationGranularity
static FORCEINLINE FFreeBlock * GetPoolHeaderFromPointer(void *Ptr)
FORCEINLINE void FreeInline(void *Ptr)
virtual bool IsInternallyThreadSafe() const override
FORCEINLINE uint32 BoundSizeToPoolIndex(SIZE_T Size)
void * ReallocExternal(void *Ptr, SIZE_T NewSize, uint32 Alignment)
void FlushCurrentThreadCache()
virtual FORCEINLINE void * Realloc(void *Ptr, SIZE_T NewSize, uint32 Alignment) override
static FMallocBinned2 * MallocBinned2
FPoolTable SmallPoolTables[BINNED2_SMALL_POOL_COUNT]
bool GetAllocationSizeExternal(void *Ptr, SIZE_T &SizeOut)
static uint32 Binned2TlsSlot
virtual void OnPostFork() override
FORCEINLINE void * ReallocInline(void *Ptr, SIZE_T NewSize, uint32 Alignment)
virtual void OnMallocInitialized() override
virtual void ClearAndDisableTLSCachesOnCurrentThread() override
virtual void Trim(bool bTrimThreadCaches) override
static FORCEINLINE bool UseSmallAlloc(SIZE_T Size, uint32 Alignment)
PoolHashBucket * HashBucketFreeList
virtual void SetupTLSCachesOnCurrentThread() override
FORCEINLINE FBundleNode * PopHead()
FORCEINLINE void PushHead(FBundleNode *Node)
FBundleNode * NextNodeInCurrentBundle
FORCEINLINE uint32 GetNumFreeRegularBlocks() const
FORCEINLINE FFreeBlock(uint32 InPageSize, uint16 InBlockSize, uint8 InPoolIndex, EBlockCanary InCanary)
EBlockCanary CanaryAndForkState
FORCEINLINE void * AllocateRegularBlock()
FORCEINLINE bool PushToFront(void *InPtr, uint32 InPoolIndex, uint32 InBlockSize)
FORCEINLINE void * PopFromFront(uint32 InPoolIndex)
bool ObtainPartial(uint32 InPoolIndex)
FBundleNode * RecyleFull(uint32 InPoolIndex)
FORCEINLINE bool CanPushToFront(uint32 InPoolIndex, uint32 InBlockSize)
FBundleNode * PopBundles(uint32 InPoolIndex)
static FORCEINLINE FPerThreadFreeBlockLists * Get()
FORCEINLINE bool CanFree(uint32 InPoolIndex, uint32 InBlockSize)
bool ObtainRecycledPartial(uint32 InPoolIndex)
FORCEINLINE bool Free(void *InPtr, uint32 InPoolIndex, uint32 InBlockSize)
FFreeBlockList FreeLists[BINNED2_SMALL_POOL_COUNT]
FPerThreadFreeBlockLists()
FORCEINLINE void * Malloc(uint32 InPoolIndex)
FBundleNode * PopBundles(uint32 InPoolIndex)
FBundleNode * RecycleFullBundle(uint32 InPoolIndex)
FPoolInfo & GetFrontPool()
const FPoolInfo & GetFrontPool() const
void ValidateExhaustedPools()
void LinkToFront(FPoolInfo *Pool)
FPoolInfo & PushNewPoolToFront(FMallocBinned2 &Allocator, uint32 InBytes, uint32 InPoolIndex)
void ValidateActivePools()
FORCEINLINE uint64 GetMaxHashBuckets() const
FORCEINLINE void GetHashBucketAndPoolIndices(const void *InPtr, uint32 &OutBucketIndex, UPTRINT &OutBucketCollision, uint32 &OutPoolIndex) const
uint64 PtrToPoolPageBitShift
FPtrToPoolMapping(uint32 InPageSize, uint64 InNumPoolsPerPage, uint64 AddressLimit)
void Init(uint32 InPageSize, uint64 InNumPoolsPerPage, uint64 AddressLimit)