From e8c9f9cc235b8c06a9a08411a9242dfafd529374 Mon Sep 17 00:00:00 2001 From: _Redstone_c_ Date: Wed, 22 Feb 2023 23:33:10 +0800 Subject: [PATCH] feat(memory): add TInlineAllocator and the corresponding testing --- .../Private/Testing/ContainersTesting.cpp | 79 +++++++----- .../Source/Public/Memory/Allocator.h | 113 +++++++++++++++++- 2 files changed, 154 insertions(+), 38 deletions(-) diff --git a/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp index bf87608..e10fbac 100644 --- a/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp +++ b/Redcraft.Utility/Source/Private/Testing/ContainersTesting.cpp @@ -14,35 +14,38 @@ void TestContainers() TestArray(); } -void TestArray() +NAMESPACE_UNNAMED_BEGIN + +template +void TestArrayTemplate() { { - TArray ArrayA; - TArray ArrayB(4); - TArray ArrayC(4, 4); - TArray ArrayD(ArrayC); - TArray ArrayE(MoveTemp(ArrayB)); - TArray ArrayF({ 0, 1, 2, 3 }); + TArray ArrayA; + TArray ArrayB(4); + TArray ArrayC(4, 4); + TArray ArrayD(ArrayC); + TArray ArrayE(MoveTemp(ArrayB)); + TArray ArrayF({ 0, 1, 2, 3 }); - TArray ArrayG; - TArray ArrayH; - TArray ArrayI; + TArray ArrayG; + TArray ArrayH; + TArray ArrayI; ArrayG = ArrayD; ArrayH = MoveTemp(ArrayE); ArrayI = { 0, 1, 2, 3 }; - always_check((ArrayC == TArray({ 4, 4, 4, 4 }))); - always_check((ArrayD == TArray({ 4, 4, 4, 4 }))); - always_check((ArrayG == TArray({ 4, 4, 4, 4 }))); - always_check((ArrayF == TArray({ 0, 1, 2, 3 }))); - always_check((ArrayI == TArray({ 0, 1, 2, 3 }))); + always_check((ArrayC == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayD == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayG == TArray({ 4, 4, 4, 4 }))); + always_check((ArrayF == TArray({ 0, 1, 2, 3 }))); + always_check((ArrayI == TArray({ 0, 1, 2, 3 }))); } { - TArray ArrayA = { 1, 2, 3 }; - TArray ArrayB = { 7, 8, 9, 10 }; - TArray ArrayC = { 1, 2, 3 }; + TArray ArrayA = { 1, 2, 3 }; + TArray ArrayB = { 7, 8, 9, 10 }; + TArray ArrayC = { 1, 2, 3 }; always_check((!(ArrayA == ArrayB))); always_check(( (ArrayA != ArrayB))); @@ -60,25 +63,25 @@ void TestArray() } { - TArray Array = { 1, 2, 3 }; + TArray Array = { 1, 2, 3 }; Array.Insert(Array.Begin() + 1, 2); - always_check((Array == TArray({ 1, 2, 2, 3 }))); + always_check((Array == TArray({ 1, 2, 2, 3 }))); Array.Insert(Array.End(), 2, 4); - always_check((Array == TArray({ 1, 2, 2, 3, 4, 4 }))); + always_check((Array == TArray({ 1, 2, 2, 3, 4, 4 }))); Array.Insert(Array.Begin(), { 1, 1, 4, 5, 1, 4 }); - always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); Array.Emplace(Array.End(), 5); - always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 }))); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4, 5 }))); Array.StableErase(Array.End() - 1); - always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3, 4, 4 }))); Array.StableErase(Array.End() - 2, Array.End()); - always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 }))); + always_check((Array == TArray({ 1, 1, 4, 5, 1, 4, 1, 2, 2, 3 }))); Array.Erase(Array.End() - 2); always_check((Array.Num() == 9)); @@ -88,33 +91,43 @@ void TestArray() } { - TArray Array = { 1, 2, 3 }; + TArray Array = { 1, 2, 3 }; Array.PushBack(4); - always_check((Array == TArray({ 1, 2, 3, 4 }))); + always_check((Array == TArray({ 1, 2, 3, 4 }))); Array.EmplaceBack(5); - always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); + always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); Array.EmplaceBack(5) = 6; - always_check((Array == TArray({ 1, 2, 3, 4, 5, 6 }))); + always_check((Array == TArray({ 1, 2, 3, 4, 5, 6 }))); Array.PopBack(); - always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); + always_check((Array == TArray({ 1, 2, 3, 4, 5 }))); Array.SetNum(4); - always_check((Array == TArray({ 1, 2, 3, 4 }))); + always_check((Array == TArray({ 1, 2, 3, 4 }))); Array.Reserve(64); always_check((Array.Num() == 4)); - always_check((Array.Max() == 64)); + always_check((Array.Max() == 64 || Array.Max() == Capacity)); Array.Shrink(); always_check((Array.Num() == 4)); - always_check((Array.Max() == 4)); + always_check((Array.Max() == 4 || Array.Max() == Capacity)); } } +NAMESPACE_UNNAMED_END + +void TestArray() +{ + TestArrayTemplate(); + TestArrayTemplate(); + TestArrayTemplate, 8>(); + TestArrayTemplate, 64>(); +} + NAMESPACE_END(Testing) NAMESPACE_MODULE_END(Utility) diff --git a/Redcraft.Utility/Source/Public/Memory/Allocator.h b/Redcraft.Utility/Source/Public/Memory/Allocator.h index b4f0ebf..693d9c9 100644 --- a/Redcraft.Utility/Source/Public/Memory/Allocator.h +++ b/Redcraft.Utility/Source/Public/Memory/Allocator.h @@ -24,8 +24,10 @@ concept CInstantiableAllocator = CDerivedFrom && !CSameA struct FAllocatorInterface { template - struct ForElementType : private FSingleton + class ForElementType : private FSingleton { + public: + /** * Allocates uninitialized storage. * Should be allocated according to the results given by the CalculateSlackReserve() family, @@ -33,13 +35,13 @@ struct FAllocatorInterface * this is to support special allocators such as TInlineAllocator. * If 'InNum' is zero, return nullptr. */ - NODISCARD FORCEINLINE T* Allocate(size_t InNum) = delete; + NODISCARD FORCEINLINE constexpr T* Allocate(size_t InNum) = delete; /** Deallocates storage. */ - FORCEINLINE void Deallocate(T* InPtr) = delete; + FORCEINLINE constexpr void Deallocate(T* InPtr) = delete; /** @return true if allocation can be deallocated by another allocator, otherwise false. */ - NODISCARD FORCEINLINE bool IsTransferable(T* InPtr) { return true; } + NODISCARD FORCEINLINE constexpr bool IsTransferable(T* InPtr) const { return true; } /** Calculates the amount of slack to allocate for an array that has just grown to a given number of elements. */ NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const = delete; @@ -57,8 +59,10 @@ struct FAllocatorInterface struct FHeapAllocator : public FAllocatorInterface { template - struct ForElementType : public FAllocatorInterface::ForElementType + class ForElementType : public FAllocatorInterface::ForElementType { + public: + NODISCARD FORCEINLINE T* Allocate(size_t InNum) { return InNum != 0 ? static_cast(Memory::Malloc(Memory::QuantizeSize(InNum * sizeof(T)), alignof(T))) : nullptr; @@ -118,6 +122,105 @@ struct FHeapAllocator : public FAllocatorInterface using FDefaultAllocator = FHeapAllocator; +/** + * The inline allocator allocates up to a specified number of elements in the same allocation as the container. + * Any allocation needed beyond that causes all data to be moved into an indirect allocation. + */ +template +struct TInlineAllocator : public FAllocatorInterface +{ + template + class ForElementType : public FAllocatorInterface::ForElementType + { + public: + + NODISCARD FORCEINLINE T* Allocate(size_t InNum) + { + if (InNum == 0) return nullptr; + + check(InNum >= NumInline); + + if (InNum == NumInline) return reinterpret_cast(&InlineStorage); + + return Secondary.Allocate(InNum); + } + + FORCEINLINE void Deallocate(T* InPtr) + { + if (InPtr == reinterpret_cast(&InlineStorage)) return; + + Secondary.Deallocate(InPtr); + } + + NODISCARD FORCEINLINE bool IsTransferable(T* InPtr) const + { + if (InPtr == reinterpret_cast(&InlineStorage)) return false; + + return Secondary.IsTransferable(InPtr); + } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const + { + check(Num > NumAllocated); + check(NumAllocated >= NumInline); + + if (Num <= NumInline) return NumInline; + + return Secondary.CalculateSlackGrow(Num, NumAllocated <= NumInline ? 0 : NumAllocated); + } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackShrink(size_t Num, size_t NumAllocated) const + { + check(Num < NumAllocated); + check(NumAllocated >= NumInline); + + if (Num <= NumInline) return NumInline; + + return Secondary.CalculateSlackShrink(Num, NumAllocated); + } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackReserve(size_t Num) const + { + if (Num <= NumInline) return NumInline; + + return Secondary.CalculateSlackReserve(Num); + } + + private: + + TAlignedStorage InlineStorage[NumInline]; + + typename SecondaryAllocator::template ForElementType Secondary; + + }; +}; + +/** This is a null allocator for which all operations are illegal. */ +struct FNullAllocator : public FAllocatorInterface +{ + template + class ForElementType : public FAllocatorInterface::ForElementType + { + public: + + NODISCARD FORCEINLINE constexpr T* Allocate(size_t InNum) { check_no_entry(); return nullptr; } + + FORCEINLINE constexpr void Deallocate(T* InPtr) { check_no_entry(); } + + NODISCARD FORCEINLINE constexpr bool IsTransferable(T* InPtr) const { check_no_entry(); return false; } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackGrow(size_t Num, size_t NumAllocated) const { check_no_entry(); return 0; } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackShrink(size_t Num, size_t NumAllocated) const { check_no_entry(); return 0; } + + NODISCARD FORCEINLINE constexpr size_t CalculateSlackReserve(size_t Num) const { check_no_entry(); return 0; } + + }; +}; + +template +using TFixedAllocator = TInlineAllocator; + NAMESPACE_MODULE_END(Utility) NAMESPACE_MODULE_END(Redcraft) NAMESPACE_REDCRAFT_END