Skip to content

Commit 0a60713

Browse files
Added benchmark for linear allocator.
1 parent dedab85 commit 0a60713

4 files changed

Lines changed: 200 additions & 18 deletions

File tree

src/Common.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,19 @@ class RandomNumberGenerator
206206
uint32_t GenerateFast() { return m_Value = (m_Value * 196314165 + 907633515); }
207207
};
208208

209+
// Wrapper for RandomNumberGenerator compatible with STL "UniformRandomNumberGenerator" idea.
210+
struct MyUniformRandomNumberGenerator
211+
{
212+
typedef uint32_t result_type;
213+
MyUniformRandomNumberGenerator(RandomNumberGenerator& gen) : m_Gen(gen) { }
214+
static uint32_t min() { return 0; }
215+
static uint32_t max() { return UINT32_MAX; }
216+
uint32_t operator()() { return m_Gen.Generate(); }
217+
218+
private:
219+
RandomNumberGenerator& m_Gen;
220+
};
221+
209222
void ReadFile(std::vector<char>& out, const char* fileName);
210223

211224
enum class CONSOLE_COLOR

src/Tests.cpp

Lines changed: 182 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,26 @@
77

88
#ifdef _WIN32
99

10+
enum CONFIG_TYPE {
11+
CONFIG_TYPE_MINIMUM,
12+
CONFIG_TYPE_SMALL,
13+
CONFIG_TYPE_AVERAGE,
14+
CONFIG_TYPE_LARGE,
15+
CONFIG_TYPE_MAXIMUM,
16+
CONFIG_TYPE_COUNT
17+
};
18+
19+
static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
20+
//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
21+
1022
enum class FREE_ORDER { FORWARD, BACKWARD, RANDOM, COUNT };
1123

24+
static const wchar_t* FREE_ORDER_NAMES[] = {
25+
L"FORWARD",
26+
L"BACKWARD",
27+
L"RANDOM",
28+
};
29+
1230
struct AllocationSize
1331
{
1432
uint32_t Probability;
@@ -1948,6 +1966,169 @@ static void ManuallyTestLinearAllocator()
19481966
vmaDestroyPool(g_hAllocator, pool);
19491967
}
19501968

1969+
static void BenchmarkLinearAllocatorCase(bool linear, bool empty, FREE_ORDER freeOrder)
1970+
{
1971+
RandomNumberGenerator rand{16223};
1972+
1973+
const VkDeviceSize bufSizeMin = 32;
1974+
const VkDeviceSize bufSizeMax = 1024;
1975+
const size_t maxBufCapacity = 10000;
1976+
const uint32_t iterationCount = 10;
1977+
1978+
VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1979+
sampleBufCreateInfo.size = bufSizeMax;
1980+
sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1981+
1982+
VmaAllocationCreateInfo sampleAllocCreateInfo = {};
1983+
sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1984+
1985+
VmaPoolCreateInfo poolCreateInfo = {};
1986+
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
1987+
assert(res == VK_SUCCESS);
1988+
1989+
poolCreateInfo.blockSize = bufSizeMax * maxBufCapacity;
1990+
if(linear)
1991+
poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
1992+
poolCreateInfo.minBlockCount = poolCreateInfo.maxBlockCount = 1;
1993+
1994+
VmaPool pool = nullptr;
1995+
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
1996+
assert(res == VK_SUCCESS);
1997+
1998+
// Buffer created just to get memory requirements. Never bound to any memory.
1999+
VkBuffer dummyBuffer = VK_NULL_HANDLE;
2000+
res = vkCreateBuffer(g_hDevice, &sampleBufCreateInfo, nullptr, &dummyBuffer);
2001+
assert(res == VK_SUCCESS && dummyBuffer);
2002+
2003+
VkMemoryRequirements memReq = {};
2004+
vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
2005+
2006+
vkDestroyBuffer(g_hDevice, dummyBuffer, nullptr);
2007+
2008+
VmaAllocationCreateInfo allocCreateInfo = {};
2009+
allocCreateInfo.pool = pool;
2010+
2011+
VmaAllocation alloc;
2012+
std::vector<VmaAllocation> baseAllocations;
2013+
2014+
if(!empty)
2015+
{
2016+
// Make allocations up to half of pool size.
2017+
VkDeviceSize totalSize = 0;
2018+
while(totalSize < poolCreateInfo.blockSize / 2)
2019+
{
2020+
memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2021+
res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2022+
assert(res == VK_SUCCESS);
2023+
baseAllocations.push_back(alloc);
2024+
totalSize += memReq.size;
2025+
}
2026+
2027+
// Delete half of them, choose randomly.
2028+
size_t allocsToDelete = baseAllocations.size() / 2;
2029+
for(size_t i = 0; i < allocsToDelete; ++i)
2030+
{
2031+
const size_t index = (size_t)rand.Generate() % baseAllocations.size();
2032+
vmaFreeMemory(g_hAllocator, baseAllocations[index]);
2033+
baseAllocations.erase(baseAllocations.begin() + index);
2034+
}
2035+
}
2036+
2037+
// BENCHMARK
2038+
const size_t allocCount = maxBufCapacity / 2;
2039+
std::vector<VmaAllocation> testAllocations;
2040+
testAllocations.reserve(allocCount);
2041+
duration allocTotalDuration = duration::zero();
2042+
duration freeTotalDuration = duration::zero();
2043+
for(uint32_t iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
2044+
{
2045+
// Allocations
2046+
time_point allocTimeBeg = std::chrono::high_resolution_clock::now();
2047+
for(size_t i = 0; i < allocCount; ++i)
2048+
{
2049+
memReq.size = bufSizeMin + rand.Generate() % (bufSizeMax - bufSizeMin);
2050+
res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);
2051+
assert(res == VK_SUCCESS);
2052+
testAllocations.push_back(alloc);
2053+
}
2054+
allocTotalDuration += std::chrono::high_resolution_clock::now() - allocTimeBeg;
2055+
2056+
// Deallocations
2057+
switch(freeOrder)
2058+
{
2059+
case FREE_ORDER::FORWARD:
2060+
// Leave testAllocations unchanged.
2061+
break;
2062+
case FREE_ORDER::BACKWARD:
2063+
std::reverse(testAllocations.begin(), testAllocations.end());
2064+
break;
2065+
case FREE_ORDER::RANDOM:
2066+
std::shuffle(testAllocations.begin(), testAllocations.end(), MyUniformRandomNumberGenerator(rand));
2067+
break;
2068+
default: assert(0);
2069+
}
2070+
2071+
time_point freeTimeBeg = std::chrono::high_resolution_clock::now();
2072+
for(size_t i = 0; i < allocCount; ++i)
2073+
vmaFreeMemory(g_hAllocator, testAllocations[i]);
2074+
freeTotalDuration += std::chrono::high_resolution_clock::now() - freeTimeBeg;
2075+
2076+
testAllocations.clear();
2077+
}
2078+
2079+
// Delete baseAllocations
2080+
while(!baseAllocations.empty())
2081+
{
2082+
vmaFreeMemory(g_hAllocator, baseAllocations.back());
2083+
baseAllocations.pop_back();
2084+
}
2085+
2086+
vmaDestroyPool(g_hAllocator, pool);
2087+
2088+
wprintf(L" LinearAlgorithm=%u %s FreeOrder=%s: allocations %g s, free %g s\n",
2089+
linear ? 1 : 0,
2090+
empty ? L"Empty" : L"Not empty",
2091+
FREE_ORDER_NAMES[(size_t)freeOrder],
2092+
ToFloatSeconds(allocTotalDuration),
2093+
ToFloatSeconds(freeTotalDuration));
2094+
}
2095+
2096+
static void BenchmarkLinearAllocator()
2097+
{
2098+
wprintf(L"Benchmark linear allocator\n");
2099+
2100+
uint32_t freeOrderCount = 1;
2101+
if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_LARGE)
2102+
freeOrderCount = 3;
2103+
else if(ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL)
2104+
freeOrderCount = 2;
2105+
2106+
const uint32_t emptyCount = ConfigType >= CONFIG_TYPE::CONFIG_TYPE_SMALL ? 2 : 1;
2107+
2108+
for(uint32_t freeOrderIndex = 0; freeOrderIndex < freeOrderCount; ++freeOrderIndex)
2109+
{
2110+
FREE_ORDER freeOrder = FREE_ORDER::COUNT;
2111+
switch(freeOrderIndex)
2112+
{
2113+
case 0: freeOrder = FREE_ORDER::BACKWARD; break;
2114+
case 1: freeOrder = FREE_ORDER::FORWARD; break;
2115+
case 2: freeOrder = FREE_ORDER::RANDOM; break;
2116+
default: assert(0);
2117+
}
2118+
2119+
for(uint32_t emptyIndex = 0; emptyIndex < emptyCount; ++emptyIndex)
2120+
{
2121+
for(uint32_t linearIndex = 0; linearIndex < 2; ++linearIndex)
2122+
{
2123+
BenchmarkLinearAllocatorCase(
2124+
linearIndex ? 1 : 0, // linear
2125+
emptyIndex ? 0 : 1, // empty
2126+
freeOrder); // freeOrder
2127+
}
2128+
}
2129+
}
2130+
}
2131+
19512132
static void TestPool_SameSize()
19522133
{
19532134
const VkDeviceSize BUF_SIZE = 1024 * 1024;
@@ -3194,17 +3375,6 @@ static void PerformCustomPoolTest(FILE* file)
31943375
WritePoolTestResult(file, "Code desc", "Test desc", config, result);
31953376
}
31963377

3197-
enum CONFIG_TYPE {
3198-
CONFIG_TYPE_MINIMUM,
3199-
CONFIG_TYPE_SMALL,
3200-
CONFIG_TYPE_AVERAGE,
3201-
CONFIG_TYPE_LARGE,
3202-
CONFIG_TYPE_MAXIMUM,
3203-
CONFIG_TYPE_COUNT
3204-
};
3205-
3206-
static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_SMALL;
3207-
//static constexpr CONFIG_TYPE ConfigType = CONFIG_TYPE_LARGE;
32083378
static const char* CODE_DESCRIPTION = "Foo";
32093379

32103380
static void PerformMainTests(FILE* file)
@@ -3687,6 +3857,7 @@ void Test()
36873857
TestMappingMultithreaded();
36883858
TestLinearAllocator();
36893859
ManuallyTestLinearAllocator();
3860+
BenchmarkLinearAllocator();
36903861
TestDefragmentationSimple();
36913862
TestDefragmentationFull();
36923863

src/VmaUsage.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@ macros if you want to configure the library and then include its header to
1616
include all public interface declarations. Example:
1717
*/
1818

19-
//#define VMA_USE_STL_CONTAINERS 1
20-
2119
//#define VMA_HEAVY_ASSERT(expr) assert(expr)
22-
20+
//#define VMA_USE_STL_CONTAINERS 1
2321
//#define VMA_DEDICATED_ALLOCATION 0
24-
2522
//#define VMA_DEBUG_MARGIN 16
2623
//#define VMA_DEBUG_DETECT_CORRUPTION 1
2724
//#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
2825
//#define VMA_RECORDING_ENABLED 0
26+
//#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY 256
2927

3028
#pragma warning(push, 4)
3129
#pragma warning(disable: 4127) // conditional expression is constant

src/vk_mem_alloc.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ you can achieve behavior of a ring buffer / queue.
641641
642642
![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
643643
644-
Pools with linear algorithm support lost allocations when used as ring buffer.
644+
Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
645645
If there is not enough free space for a new allocation, but existing allocations
646646
from the front of the queue can become lost, they become lost and the allocation
647647
succeeds.
@@ -8333,7 +8333,7 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest(
83338333
for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
83348334
{
83358335
const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
8336-
if(VmaBlocksOnSamePage(nextSuballoc.offset, nextSuballoc.size, resultOffset, bufferImageGranularity))
8336+
if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
83378337
{
83388338
if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
83398339
{
@@ -8364,7 +8364,7 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest(
83648364
for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
83658365
{
83668366
const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
8367-
if(VmaBlocksOnSamePage(resultOffset, allocSize, prevSuballoc.offset, bufferImageGranularity))
8367+
if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
83688368
{
83698369
if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
83708370
{

0 commit comments

Comments
 (0)