Refactor memory allocators to use dedicated path when moving collection data that is not blittable

#2001 #1920
This commit is contained in:
Wojtek Figat
2023-11-28 16:02:36 +01:00
parent 0aeac36f09
commit 4f8aff4352
6 changed files with 132 additions and 35 deletions

View File

@@ -25,6 +25,19 @@ private:
int32 _capacity; int32 _capacity;
AllocationData _allocation; AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromCount, int32 fromCapacity)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
else
{
to.Allocate(fromCapacity);
Memory::MoveItems(to.Get(), from.Get(), fromCount);
Memory::DestructItems(from.Get(), fromCount);
from.Free();
}
}
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Array"/> class. /// Initializes a new instance of the <see cref="Array"/> class.
@@ -134,7 +147,7 @@ public:
_capacity = other._capacity; _capacity = other._capacity;
other._count = 0; other._count = 0;
other._capacity = 0; other._capacity = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _count, _capacity);
} }
/// <summary> /// <summary>
@@ -191,7 +204,7 @@ public:
_capacity = other._capacity; _capacity = other._capacity;
other._count = 0; other._count = 0;
other._capacity = 0; other._capacity = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _count, _capacity);
} }
return *this; return *this;
} }
@@ -713,9 +726,18 @@ public:
/// <param name="other">The other collection.</param> /// <param name="other">The other collection.</param>
void Swap(Array& other) void Swap(Array& other)
{ {
::Swap(_count, other._count); if IF_CONSTEXPR (AllocationType::HasSwap)
::Swap(_capacity, other._capacity); {
_allocation.Swap(other._allocation); _allocation.Swap(other._allocation);
::Swap(_count, other._count);
::Swap(_capacity, other._capacity);
}
else
{
Array tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
} }
/// <summary> /// <summary>
@@ -726,9 +748,7 @@ public:
T* data = _allocation.Get(); T* data = _allocation.Get();
const int32 count = _count / 2; const int32 count = _count / 2;
for (int32 i = 0; i < count; i++) for (int32 i = 0; i < count; i++)
{
::Swap(data[i], data[_count - i - 1]); ::Swap(data[i], data[_count - i - 1]);
}
} }
public: public:

View File

@@ -110,6 +110,33 @@ private:
int32 _size = 0; int32 _size = 0;
AllocationData _allocation; AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
else
{
to.Allocate(fromSize);
Bucket* toData = to.Get();
Bucket* fromData = from.Get();
for (int32 i = 0; i < fromSize; i++)
{
Bucket& fromBucket = fromData[i];
if (fromBucket.IsOccupied())
{
Bucket& toBucket = toData[i];
Memory::MoveItems(&toBucket.Key, &fromBucket.Key, 1);
Memory::MoveItems(&toBucket.Value, &fromBucket.Value, 1);
toBucket._state = Bucket::Occupied;
Memory::DestructItem(&fromBucket.Key);
Memory::DestructItem(&fromBucket.Value);
fromBucket._state = Bucket::Empty;
}
}
from.Free();
}
}
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Dictionary"/> class. /// Initializes a new instance of the <see cref="Dictionary"/> class.
@@ -139,7 +166,7 @@ public:
other._elementsCount = 0; other._elementsCount = 0;
other._deletedCount = 0; other._deletedCount = 0;
other._size = 0; other._size = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _size);
} }
/// <summary> /// <summary>
@@ -180,7 +207,7 @@ public:
other._elementsCount = 0; other._elementsCount = 0;
other._deletedCount = 0; other._deletedCount = 0;
other._size = 0; other._size = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _size);
} }
return *this; return *this;
} }
@@ -510,7 +537,7 @@ public:
return; return;
ASSERT(capacity >= 0); ASSERT(capacity >= 0);
AllocationData oldAllocation; AllocationData oldAllocation;
oldAllocation.Swap(_allocation); MoveToEmpty(oldAllocation, _allocation, _size);
const int32 oldSize = _size; const int32 oldSize = _size;
const int32 oldElementsCount = _elementsCount; const int32 oldElementsCount = _elementsCount;
_deletedCount = _elementsCount = 0; _deletedCount = _elementsCount = 0;
@@ -580,10 +607,19 @@ public:
/// <param name="other">The other collection.</param> /// <param name="other">The other collection.</param>
void Swap(Dictionary& other) void Swap(Dictionary& other)
{ {
::Swap(_elementsCount, other._elementsCount); if IF_CONSTEXPR (AllocationType::HasSwap)
::Swap(_deletedCount, other._deletedCount); {
::Swap(_size, other._size); ::Swap(_elementsCount, other._elementsCount);
_allocation.Swap(other._allocation); ::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
}
else
{
Dictionary tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
} }
public: public:
@@ -930,7 +966,7 @@ private:
{ {
// Rebuild entire table completely // Rebuild entire table completely
AllocationData oldAllocation; AllocationData oldAllocation;
oldAllocation.Swap(_allocation); MoveToEmpty(oldAllocation, _allocation, _size);
_allocation.Allocate(_size); _allocation.Allocate(_size);
Bucket* data = _allocation.Get(); Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++) for (int32 i = 0; i < _size; i++)

View File

@@ -93,6 +93,31 @@ private:
int32 _size = 0; int32 _size = 0;
AllocationData _allocation; AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
else
{
to.Allocate(fromSize);
Bucket* toData = to.Get();
Bucket* fromData = from.Get();
for (int32 i = 0; i < fromSize; i++)
{
Bucket& fromBucket = fromData[i];
if (fromBucket.IsOccupied())
{
Bucket& toBucket = toData[i];
Memory::MoveItems(&toBucket.Item, &fromBucket.Item, 1);
toBucket._state = Bucket::Occupied;
Memory::DestructItem(&fromBucket.Item);
fromBucket._state = Bucket::Empty;
}
}
from.Free();
}
}
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HashSet"/> class. /// Initializes a new instance of the <see cref="HashSet"/> class.
@@ -122,7 +147,7 @@ public:
other._elementsCount = 0; other._elementsCount = 0;
other._deletedCount = 0; other._deletedCount = 0;
other._size = 0; other._size = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _size);
} }
/// <summary> /// <summary>
@@ -163,7 +188,7 @@ public:
other._elementsCount = 0; other._elementsCount = 0;
other._deletedCount = 0; other._deletedCount = 0;
other._size = 0; other._size = 0;
_allocation.Swap(other._allocation); MoveToEmpty(_allocation, other._allocation, _size);
} }
return *this; return *this;
} }
@@ -389,7 +414,7 @@ public:
return; return;
ASSERT(capacity >= 0); ASSERT(capacity >= 0);
AllocationData oldAllocation; AllocationData oldAllocation;
oldAllocation.Swap(_allocation); MoveToEmpty(oldAllocation, _allocation, _size);
const int32 oldSize = _size; const int32 oldSize = _size;
const int32 oldElementsCount = _elementsCount; const int32 oldElementsCount = _elementsCount;
_deletedCount = _elementsCount = 0; _deletedCount = _elementsCount = 0;
@@ -458,10 +483,19 @@ public:
/// <param name="other">The other collection.</param> /// <param name="other">The other collection.</param>
void Swap(HashSet& other) void Swap(HashSet& other)
{ {
::Swap(_elementsCount, other._elementsCount); if IF_CONSTEXPR (AllocationType::HasSwap)
::Swap(_deletedCount, other._deletedCount); {
::Swap(_size, other._size); ::Swap(_elementsCount, other._elementsCount);
_allocation.Swap(other._allocation); ::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
}
else
{
HashSet tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
} }
public: public:
@@ -726,7 +760,7 @@ private:
{ {
// Rebuild entire table completely // Rebuild entire table completely
AllocationData oldAllocation; AllocationData oldAllocation;
oldAllocation.Swap(_allocation); MoveToEmpty(oldAllocation, _allocation, _size);
_allocation.Allocate(_size); _allocation.Allocate(_size);
Bucket* data = _allocation.Get(); Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++) for (int32 i = 0; i < _size; i++)

View File

@@ -93,3 +93,10 @@
#endif #endif
#define PACK_STRUCT(__Declaration__) PACK_BEGIN() __Declaration__ PACK_END() #define PACK_STRUCT(__Declaration__) PACK_BEGIN() __Declaration__ PACK_END()
// C++ 17
#if __cplusplus >= 201703L
#define IF_CONSTEXPR constexpr
#else
#define IF_CONSTEXPR
#endif

View File

@@ -12,6 +12,8 @@ template<int Capacity>
class FixedAllocation class FixedAllocation
{ {
public: public:
enum { HasSwap = false };
template<typename T> template<typename T>
class Data class Data
{ {
@@ -61,12 +63,9 @@ public:
{ {
} }
FORCE_INLINE void Swap(Data& other) void Swap(Data& other)
{ {
byte tmp[Capacity * sizeof(T)]; // Not supported
Platform::MemoryCopy(tmp, _data, Capacity * sizeof(T));
Platform::MemoryCopy(_data, other._data, Capacity * sizeof(T));
Platform::MemoryCopy(other._data, tmp, Capacity * sizeof(T));
} }
}; };
}; };
@@ -77,6 +76,8 @@ public:
class HeapAllocation class HeapAllocation
{ {
public: public:
enum { HasSwap = true };
template<typename T> template<typename T>
class Data class Data
{ {
@@ -179,6 +180,8 @@ template<int Capacity, typename OtherAllocator = HeapAllocation>
class InlinedAllocation class InlinedAllocation
{ {
public: public:
enum { HasSwap = false };
template<typename T> template<typename T>
class Data class Data
{ {
@@ -267,14 +270,9 @@ public:
} }
} }
FORCE_INLINE void Swap(Data& other) void Swap(Data& other)
{ {
byte tmp[Capacity * sizeof(T)]; // Not supported
Platform::MemoryCopy(tmp, _data, Capacity * sizeof(T));
Platform::MemoryCopy(_data, other._data, Capacity * sizeof(T));
Platform::MemoryCopy(other._data, tmp, Capacity * sizeof(T));
::Swap(_useOther, other._useOther);
_other.Swap(other._other);
} }
}; };
}; };

View File

@@ -11,6 +11,8 @@ public:
static FLAXENGINE_API void* Allocate(uintptr size); static FLAXENGINE_API void* Allocate(uintptr size);
static FLAXENGINE_API void Free(void* ptr, uintptr size); static FLAXENGINE_API void Free(void* ptr, uintptr size);
enum { HasSwap = true };
template<typename T> template<typename T>
class Data class Data
{ {