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;
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:
/// <summary>
/// Initializes a new instance of the <see cref="Array"/> class.
@@ -134,7 +147,7 @@ public:
_capacity = other._capacity;
other._count = 0;
other._capacity = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _count, _capacity);
}
/// <summary>
@@ -191,7 +204,7 @@ public:
_capacity = other._capacity;
other._count = 0;
other._capacity = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _count, _capacity);
}
return *this;
}
@@ -713,9 +726,18 @@ public:
/// <param name="other">The other collection.</param>
void Swap(Array& other)
{
::Swap(_count, other._count);
::Swap(_capacity, other._capacity);
_allocation.Swap(other._allocation);
if IF_CONSTEXPR (AllocationType::HasSwap)
{
_allocation.Swap(other._allocation);
::Swap(_count, other._count);
::Swap(_capacity, other._capacity);
}
else
{
Array tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
}
/// <summary>
@@ -726,9 +748,7 @@ public:
T* data = _allocation.Get();
const int32 count = _count / 2;
for (int32 i = 0; i < count; i++)
{
::Swap(data[i], data[_count - i - 1]);
}
}
public:

View File

@@ -110,6 +110,33 @@ private:
int32 _size = 0;
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:
/// <summary>
/// Initializes a new instance of the <see cref="Dictionary"/> class.
@@ -139,7 +166,7 @@ public:
other._elementsCount = 0;
other._deletedCount = 0;
other._size = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _size);
}
/// <summary>
@@ -180,7 +207,7 @@ public:
other._elementsCount = 0;
other._deletedCount = 0;
other._size = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _size);
}
return *this;
}
@@ -510,7 +537,7 @@ public:
return;
ASSERT(capacity >= 0);
AllocationData oldAllocation;
oldAllocation.Swap(_allocation);
MoveToEmpty(oldAllocation, _allocation, _size);
const int32 oldSize = _size;
const int32 oldElementsCount = _elementsCount;
_deletedCount = _elementsCount = 0;
@@ -580,10 +607,19 @@ public:
/// <param name="other">The other collection.</param>
void Swap(Dictionary& other)
{
::Swap(_elementsCount, other._elementsCount);
::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
if IF_CONSTEXPR (AllocationType::HasSwap)
{
::Swap(_elementsCount, other._elementsCount);
::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
}
else
{
Dictionary tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
}
public:
@@ -930,7 +966,7 @@ private:
{
// Rebuild entire table completely
AllocationData oldAllocation;
oldAllocation.Swap(_allocation);
MoveToEmpty(oldAllocation, _allocation, _size);
_allocation.Allocate(_size);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)

View File

@@ -93,6 +93,31 @@ private:
int32 _size = 0;
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:
/// <summary>
/// Initializes a new instance of the <see cref="HashSet"/> class.
@@ -122,7 +147,7 @@ public:
other._elementsCount = 0;
other._deletedCount = 0;
other._size = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _size);
}
/// <summary>
@@ -163,7 +188,7 @@ public:
other._elementsCount = 0;
other._deletedCount = 0;
other._size = 0;
_allocation.Swap(other._allocation);
MoveToEmpty(_allocation, other._allocation, _size);
}
return *this;
}
@@ -389,7 +414,7 @@ public:
return;
ASSERT(capacity >= 0);
AllocationData oldAllocation;
oldAllocation.Swap(_allocation);
MoveToEmpty(oldAllocation, _allocation, _size);
const int32 oldSize = _size;
const int32 oldElementsCount = _elementsCount;
_deletedCount = _elementsCount = 0;
@@ -458,10 +483,19 @@ public:
/// <param name="other">The other collection.</param>
void Swap(HashSet& other)
{
::Swap(_elementsCount, other._elementsCount);
::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
if IF_CONSTEXPR (AllocationType::HasSwap)
{
::Swap(_elementsCount, other._elementsCount);
::Swap(_deletedCount, other._deletedCount);
::Swap(_size, other._size);
_allocation.Swap(other._allocation);
}
else
{
HashSet tmp = MoveTemp(other);
other = *this;
*this = MoveTemp(tmp);
}
}
public:
@@ -726,7 +760,7 @@ private:
{
// Rebuild entire table completely
AllocationData oldAllocation;
oldAllocation.Swap(_allocation);
MoveToEmpty(oldAllocation, _allocation, _size);
_allocation.Allocate(_size);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)

View File

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

View File

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