mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2024-11-05 12:20:07 +00:00
VmaBlockMetadata_Buddy: Fixed reporting of space wasted due to internal fragmentation as unused blocks. Added test for multi-block pool with buddy algorithm.
This commit is contained in:
parent
21017c6cbe
commit
8092715d2c
@ -18,8 +18,8 @@ enum CONFIG_TYPE {
|
|||||||
CONFIG_TYPE_COUNT
|
CONFIG_TYPE_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
|
//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
|
||||||
//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
|
static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
|
||||||
|
|
||||||
enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
|
enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
|
||||||
|
|
||||||
@ -29,6 +29,23 @@ static const char* FREE_ORDER_NAMES[] = {
|
|||||||
"RANDOM",
|
"RANDOM",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Copy of internal VmaAlgorithmToStr.
|
||||||
|
static const char* AlgorithmToStr(uint32_t algorithm)
|
||||||
|
{
|
||||||
|
switch(algorithm)
|
||||||
|
{
|
||||||
|
case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
|
||||||
|
return "Linear";
|
||||||
|
case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
|
||||||
|
return "Buddy";
|
||||||
|
case 0:
|
||||||
|
return "Default";
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AllocationSize
|
struct AllocationSize
|
||||||
{
|
{
|
||||||
uint32_t Probability;
|
uint32_t Probability;
|
||||||
@ -2131,8 +2148,8 @@ static void ManuallyTestLinearAllocator()
|
|||||||
vmaDestroyPool(g_hAllocator, pool);
|
vmaDestroyPool(g_hAllocator, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BenchmarkLinearAllocatorCase(FILE* file,
|
static void BenchmarkAlgorithmsCase(FILE* file,
|
||||||
bool linear,
|
uint32_t algorithm,
|
||||||
bool empty,
|
bool empty,
|
||||||
VmaAllocationCreateFlags allocStrategy,
|
VmaAllocationCreateFlags allocStrategy,
|
||||||
FREE_ORDER freeOrder)
|
FREE_ORDER freeOrder)
|
||||||
@ -2156,8 +2173,7 @@ static void BenchmarkLinearAllocatorCase(FILE* file,
|
|||||||
assert(res == VK_SUCCESS);
|
assert(res == VK_SUCCESS);
|
||||||
|
|
||||||
poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
|
poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
|
||||||
if(linear)
|
poolCreateInfo.flags |= algorithm;
|
||||||
poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
|
|
||||||
poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
|
poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
|
||||||
|
|
||||||
VmaPool pool = nullptr;
|
VmaPool pool = nullptr;
|
||||||
@ -2258,8 +2274,8 @@ static void BenchmarkLinearAllocatorCase(FILE* file,
|
|||||||
const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
|
const float allocTotalSeconds = ToFloatSeconds(allocTotalDuration);
|
||||||
const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
|
const float freeTotalSeconds = ToFloatSeconds(freeTotalDuration);
|
||||||
|
|
||||||
printf(" LinearAlgorithm=%u %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
|
printf(" Algorithm=%s %s Allocation=%s FreeOrder=%s: allocations %g s, free %g s\n",
|
||||||
linear ? 1 : 0,
|
AlgorithmToStr(algorithm),
|
||||||
empty ? "Empty" : "Not empty",
|
empty ? "Empty" : "Not empty",
|
||||||
GetAllocationStrategyName(allocStrategy),
|
GetAllocationStrategyName(allocStrategy),
|
||||||
FREE_ORDER_NAMES[(size_t)freeOrder],
|
FREE_ORDER_NAMES[(size_t)freeOrder],
|
||||||
@ -2271,9 +2287,9 @@ static void BenchmarkLinearAllocatorCase(FILE* file,
|
|||||||
std::string currTime;
|
std::string currTime;
|
||||||
CurrentTimeToStr(currTime);
|
CurrentTimeToStr(currTime);
|
||||||
|
|
||||||
fprintf(file, "%s,%s,%u,%u,%s,%s,%g,%g\n",
|
fprintf(file, "%s,%s,%s,%u,%s,%s,%g,%g\n",
|
||||||
CODE_DESCRIPTION, currTime.c_str(),
|
CODE_DESCRIPTION, currTime.c_str(),
|
||||||
linear ? 1 : 0,
|
AlgorithmToStr(algorithm),
|
||||||
empty ? 1 : 0,
|
empty ? 1 : 0,
|
||||||
GetAllocationStrategyName(allocStrategy),
|
GetAllocationStrategyName(allocStrategy),
|
||||||
FREE_ORDER_NAMES[(uint32_t)freeOrder],
|
FREE_ORDER_NAMES[(uint32_t)freeOrder],
|
||||||
@ -2282,15 +2298,15 @@ static void BenchmarkLinearAllocatorCase(FILE* file,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BenchmarkLinearAllocator(FILE* file)
|
static void BenchmarkAlgorithms(FILE* file)
|
||||||
{
|
{
|
||||||
wprintf(L"Benchmark linear allocator\n");
|
wprintf(L"Benchmark algorithms\n");
|
||||||
|
|
||||||
if(file)
|
if(file)
|
||||||
{
|
{
|
||||||
fprintf(file,
|
fprintf(file,
|
||||||
"Code,Time,"
|
"Code,Time,"
|
||||||
"Linear,Empty,Allocation strategy,Free order,"
|
"Algorithm,Empty,Allocation strategy,Free order,"
|
||||||
"Allocation time (s),Deallocation time (s)\n");
|
"Allocation time (s),Deallocation time (s)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2316,15 +2332,28 @@ static void BenchmarkLinearAllocator(FILE* file)
|
|||||||
|
|
||||||
for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
|
for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
|
||||||
{
|
{
|
||||||
for(uint32_t linearIndex = 0; linearIndex < 2; ++linearIndex)
|
for(uint32_t algorithmIndex = 0; algorithmIndex < 3; ++algorithmIndex)
|
||||||
{
|
{
|
||||||
const bool linear = linearIndex ? 1 : 0;
|
uint32_t algorithm = 0;
|
||||||
|
switch(algorithmIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
algorithm = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
algorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t currAllocStrategyCount = linear ? 1 : allocStrategyCount;
|
uint32_t currAllocStrategyCount = algorithm != 0 ? 1 : allocStrategyCount;
|
||||||
for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
|
for(uint32_t allocStrategyIndex = 0; allocStrategyIndex < currAllocStrategyCount; ++allocStrategyIndex)
|
||||||
{
|
{
|
||||||
VmaAllocatorCreateFlags strategy = 0;
|
VmaAllocatorCreateFlags strategy = 0;
|
||||||
if(!linear)
|
if(currAllocStrategyCount > 1)
|
||||||
{
|
{
|
||||||
switch(allocStrategyIndex)
|
switch(allocStrategyIndex)
|
||||||
{
|
{
|
||||||
@ -2335,9 +2364,9 @@ static void BenchmarkLinearAllocator(FILE* file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BenchmarkLinearAllocatorCase(
|
BenchmarkAlgorithmsCase(
|
||||||
file,
|
file,
|
||||||
linear, // linear
|
algorithm,
|
||||||
emptyIndex ? 0 : 1, // empty
|
emptyIndex ? 0 : 1, // empty
|
||||||
strategy,
|
strategy,
|
||||||
freeOrder); // freeOrder
|
freeOrder); // freeOrder
|
||||||
@ -4098,7 +4127,7 @@ static void BasicTestBuddyAllocator()
|
|||||||
|
|
||||||
poolCreateInfo.blockSize = 1024 * 1024;
|
poolCreateInfo.blockSize = 1024 * 1024;
|
||||||
poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
|
poolCreateInfo.flags = VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT;
|
||||||
poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
|
//poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
|
||||||
|
|
||||||
VmaPool pool = nullptr;
|
VmaPool pool = nullptr;
|
||||||
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
|
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
|
||||||
@ -4131,12 +4160,23 @@ static void BasicTestBuddyAllocator()
|
|||||||
assert(res == VK_SUCCESS);
|
assert(res == VK_SUCCESS);
|
||||||
bufInfo.push_back(newBufInfo);
|
bufInfo.push_back(newBufInfo);
|
||||||
|
|
||||||
SaveAllocatorStatsToFile(L"BuddyTest01.json");
|
|
||||||
|
|
||||||
VmaPoolStats stats = {};
|
VmaPoolStats stats = {};
|
||||||
vmaGetPoolStats(g_hAllocator, pool, &stats);
|
vmaGetPoolStats(g_hAllocator, pool, &stats);
|
||||||
int DBG = 0; // Set breakpoint here to inspect `stats`.
|
int DBG = 0; // Set breakpoint here to inspect `stats`.
|
||||||
|
|
||||||
|
// Allocate enough new buffers to surely fall into second block.
|
||||||
|
for(uint32_t i = 0; i < 32; ++i)
|
||||||
|
{
|
||||||
|
bufCreateInfo.size = 1024 * (rand.Generate() % 32 + 1);
|
||||||
|
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
|
||||||
|
&newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
|
||||||
|
assert(res == VK_SUCCESS);
|
||||||
|
bufInfo.push_back(newBufInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveAllocatorStatsToFile(L"BuddyTest01.json");
|
||||||
|
|
||||||
// Destroy the buffers in random order.
|
// Destroy the buffers in random order.
|
||||||
while(!bufInfo.empty())
|
while(!bufInfo.empty())
|
||||||
{
|
{
|
||||||
@ -4158,6 +4198,7 @@ void Test()
|
|||||||
// # Temporarily insert custom tests here
|
// # Temporarily insert custom tests here
|
||||||
// ########################################
|
// ########################################
|
||||||
// ########################################
|
// ########################################
|
||||||
|
|
||||||
BasicTestBuddyAllocator();
|
BasicTestBuddyAllocator();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4186,9 +4227,7 @@ void Test()
|
|||||||
FILE* file;
|
FILE* file;
|
||||||
fopen_s(&file, "LinearAllocator.csv", "w");
|
fopen_s(&file, "LinearAllocator.csv", "w");
|
||||||
assert(file != NULL);
|
assert(file != NULL);
|
||||||
|
BenchmarkAlgorithms(file);
|
||||||
BenchmarkLinearAllocator(file);
|
|
||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5051,7 +5051,9 @@ private:
|
|||||||
} m_FreeList[MAX_LEVELS];
|
} m_FreeList[MAX_LEVELS];
|
||||||
// Number of nodes in the tree with type == TYPE_ALLOCATION.
|
// Number of nodes in the tree with type == TYPE_ALLOCATION.
|
||||||
size_t m_AllocationCount;
|
size_t m_AllocationCount;
|
||||||
|
// Number of nodes in the tree with type == TYPE_FREE.
|
||||||
size_t m_FreeCount;
|
size_t m_FreeCount;
|
||||||
|
// This includes space wasted due to internal fragmentation.
|
||||||
VkDeviceSize m_SumFreeSize;
|
VkDeviceSize m_SumFreeSize;
|
||||||
|
|
||||||
void DeleteNode(Node* node);
|
void DeleteNode(Node* node);
|
||||||
@ -9258,7 +9260,7 @@ void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
|
|||||||
outInfo.blockCount = 1;
|
outInfo.blockCount = 1;
|
||||||
|
|
||||||
outInfo.allocationCount = outInfo.unusedRangeCount = 0;
|
outInfo.allocationCount = outInfo.unusedRangeCount = 0;
|
||||||
outInfo.unusedBytes = outInfo.unusedBytes = 0;
|
outInfo.usedBytes = outInfo.unusedBytes = 0;
|
||||||
|
|
||||||
outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
|
outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
|
||||||
outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
|
outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
|
||||||
@ -9392,9 +9394,10 @@ void VmaBlockMetadata_Buddy::Alloc(
|
|||||||
AddToFreeListFront(childrenLevel, rightChild);
|
AddToFreeListFront(childrenLevel, rightChild);
|
||||||
AddToFreeListFront(childrenLevel, leftChild);
|
AddToFreeListFront(childrenLevel, leftChild);
|
||||||
|
|
||||||
|
++m_FreeCount;
|
||||||
|
m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
|
||||||
++currLevel;
|
++currLevel;
|
||||||
currNode = m_FreeList[currLevel].front;
|
currNode = m_FreeList[currLevel].front;
|
||||||
++m_FreeCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from free list.
|
// Remove from free list.
|
||||||
@ -9407,7 +9410,7 @@ void VmaBlockMetadata_Buddy::Alloc(
|
|||||||
|
|
||||||
++m_AllocationCount;
|
++m_AllocationCount;
|
||||||
--m_FreeCount;
|
--m_FreeCount;
|
||||||
m_SumFreeSize -= LevelToNodeSize(currLevel);
|
m_SumFreeSize -= allocSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
|
void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
|
||||||
@ -9435,6 +9438,7 @@ bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* pa
|
|||||||
break;
|
break;
|
||||||
case Node::TYPE_ALLOCATION:
|
case Node::TYPE_ALLOCATION:
|
||||||
++ctx.calculatedAllocationCount;
|
++ctx.calculatedAllocationCount;
|
||||||
|
ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
|
||||||
VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
|
VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
|
||||||
break;
|
break;
|
||||||
case Node::TYPE_SPLIT:
|
case Node::TYPE_SPLIT:
|
||||||
@ -9517,7 +9521,7 @@ void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offs
|
|||||||
|
|
||||||
++m_FreeCount;
|
++m_FreeCount;
|
||||||
--m_AllocationCount;
|
--m_AllocationCount;
|
||||||
m_SumFreeSize += levelSize;
|
m_SumFreeSize += alloc->GetSize();
|
||||||
|
|
||||||
node->type = Node::TYPE_FREE;
|
node->type = Node::TYPE_FREE;
|
||||||
|
|
||||||
@ -9533,6 +9537,7 @@ void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offs
|
|||||||
|
|
||||||
node = parent;
|
node = parent;
|
||||||
--level;
|
--level;
|
||||||
|
m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
|
||||||
--m_FreeCount;
|
--m_FreeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9550,10 +9555,22 @@ void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, co
|
|||||||
outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
|
outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
|
||||||
break;
|
break;
|
||||||
case Node::TYPE_ALLOCATION:
|
case Node::TYPE_ALLOCATION:
|
||||||
++outInfo.allocationCount;
|
{
|
||||||
outInfo.usedBytes += levelNodeSize;
|
const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
|
||||||
outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, levelNodeSize);
|
++outInfo.allocationCount;
|
||||||
outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, levelNodeSize);
|
outInfo.usedBytes += allocSize;
|
||||||
|
outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
|
||||||
|
outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
|
||||||
|
|
||||||
|
const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
|
||||||
|
if(unusedRangeSize > 0)
|
||||||
|
{
|
||||||
|
++outInfo.unusedRangeCount;
|
||||||
|
outInfo.unusedBytes += unusedRangeSize;
|
||||||
|
outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
|
||||||
|
outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Node::TYPE_SPLIT:
|
case Node::TYPE_SPLIT:
|
||||||
{
|
{
|
||||||
@ -9631,7 +9648,14 @@ void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, con
|
|||||||
PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
|
PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
|
||||||
break;
|
break;
|
||||||
case Node::TYPE_ALLOCATION:
|
case Node::TYPE_ALLOCATION:
|
||||||
PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
|
{
|
||||||
|
PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
|
||||||
|
const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
|
||||||
|
if(allocSize < levelNodeSize)
|
||||||
|
{
|
||||||
|
PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Node::TYPE_SPLIT:
|
case Node::TYPE_SPLIT:
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user