Skip to content

Commit c6432d1

Browse files
Buddy allocation algorithm finished and documented!
1 parent 6f09752 commit c6432d1

9 files changed

Lines changed: 211 additions & 143 deletions

docs/gfx/Buddy_allocator.png

3.83 KB
Loading

docs/html/custom_memory_pools.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,23 @@ <h2><a class="anchor" id="linear_algorithm_ring_buffer"></a>
141141
<div class="image">
142142
<img src="../gfx/Linear_allocator_6_ring_buffer_lost.png" alt="Ring buffer with lost allocations"/>
143143
</div>
144-
<p>Ring buffer is available only in pools with one memory block - <a class="el" href="struct_vma_pool_create_info.html#ae41142f2834fcdc82baa4883c187b75c" title="Maximum number of blocks that can be allocated in this pool. Optional. ">VmaPoolCreateInfo::maxBlockCount</a> must be 1. Otherwise behavior is undefined. </p>
144+
<p>Ring buffer is available only in pools with one memory block - <a class="el" href="struct_vma_pool_create_info.html#ae41142f2834fcdc82baa4883c187b75c" title="Maximum number of blocks that can be allocated in this pool. Optional. ">VmaPoolCreateInfo::maxBlockCount</a> must be 1. Otherwise behavior is undefined.</p>
145+
<h1><a class="anchor" id="buddy_algorithm"></a>
146+
Buddy allocation algorithm</h1>
147+
<p>There is another allocation algorithm that can be used with custom pools, called "buddy". Its internal data structure is based on a tree of blocks, each having size that is a power of two and a half of its parent's size. When you want to allocate memory of certain size, a free node in the tree is located. If it's too large, it is recursively split into two halves (called "buddies"). However, if requested allocation size is not a power of two, the size of a tree node is aligned up to the nearest power of two and the remaining space is wasted. When two buddy nodes become free, they are merged back into one larger node.</p>
148+
<div class="image">
149+
<img src="../gfx/Buddy_allocator.png" alt="Buddy allocator"/>
150+
</div>
151+
<p>The advantage of buddy allocation algorithm over default algorithm is faster allocation and deallocation, as well as smaller external fragmentation. The disadvantage is more wasted space (internal fragmentation).</p>
152+
<p>For more information, please read <a href="https://en.wikipedia.org/wiki/Buddy_memory_allocation">"Buddy memory allocation" on Wikipedia</a> or other sources that describe this concept in general.</p>
153+
<p>To use buddy allocation algorithm with a custom pool, add flag <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a97a0dc38e5161b780594d998d313d35e" title="Enables alternative, buddy allocation algorithm in this pool. ">VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT</a> to <a class="el" href="struct_vma_pool_create_info.html#a8405139f63d078340ae74513a59f5446" title="Use combination of VmaPoolCreateFlagBits. ">VmaPoolCreateInfo::flags</a> while creating <a class="el" href="struct_vma_pool.html" title="Represents custom memory pool. ">VmaPool</a> object.</p>
154+
<p>Several limitations apply to pools that use buddy algorithm:</p>
155+
<ul>
156+
<li>It is recommended to use <a class="el" href="struct_vma_pool_create_info.html#aa4265160536cdb9be821b7686c16c676" title="Size of a single VkDeviceMemory block to be allocated as part of this pool, in bytes. Optional. ">VmaPoolCreateInfo::blockSize</a> that is a power of two. Otherwise, only largest power of two smaller than the size is used for allocations. The remaining space always stays unused.</li>
157+
<li><a class="el" href="debugging_memory_usage.html#debugging_memory_usage_margins">Margins</a> and <a class="el" href="debugging_memory_usage.html#debugging_memory_usage_corruption_detection">corruption detection</a> don't work in such pools.</li>
158+
<li><a class="el" href="lost_allocations.html">Lost allocations</a> don't work in such pools. You can use them, but they never become lost. Support may be added in the future.</li>
159+
<li><a class="el" href="defragmentation.html">Defragmentation</a> doesn't work with allocations made from such pool. </li>
160+
</ul>
145161
</div></div><!-- contents -->
146162
<!-- start footer part -->
147163
<hr class="footer"/><address class="footer"><small>

docs/html/debugging_memory_usage.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ <h1><a class="anchor" id="debugging_memory_usage_margins"></a>
8989
<p>If your bug goes away after enabling margins, it means it may be caused by memory being overwritten outside of allocation boundaries. It is not 100% certain though. Change in application behavior may also be caused by different order and distribution of allocations across memory blocks after margins are applied.</p>
9090
<p>The margin is applied also before first and after last allocation in a block. It may occur only once between two adjacent allocations.</p>
9191
<p>Margins work with all types of memory.</p>
92-
<p>Margin is applied only to allocations made out of memory blocks and not to dedicated allocations, which have their own memory block of specific size. It is thus not applied to allocations made using <a class="el" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a3fc311d855c2ff53f1090ef5c722b38f" title="Set this flag if the allocation should have its own memory block. ">VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT</a> flag or those automatically decided to put into dedicated allocations, e.g. due to its large size or recommended by VK_KHR_dedicated_allocation extension.</p>
92+
<p>Margin is applied only to allocations made out of memory blocks and not to dedicated allocations, which have their own memory block of specific size. It is thus not applied to allocations made using <a class="el" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a3fc311d855c2ff53f1090ef5c722b38f" title="Set this flag if the allocation should have its own memory block. ">VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT</a> flag or those automatically decided to put into dedicated allocations, e.g. due to its large size or recommended by VK_KHR_dedicated_allocation extension. Margins are also not active in custom pools created with <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a97a0dc38e5161b780594d998d313d35e" title="Enables alternative, buddy allocation algorithm in this pool. ">VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT</a> flag.</p>
9393
<p>Margins appear in <a class="el" href="statistics.html#statistics_json_dump">JSON dump</a> as part of free space.</p>
9494
<p>Note that enabling margins increases memory usage and fragmentation.</p>
9595
<h1><a class="anchor" id="debugging_memory_usage_corruption_detection"></a>

docs/html/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ <h1><a class="anchor" id="main_table_of_contents"></a>
102102
<li><a class="el" href="custom_memory_pools.html#linear_algorithm_ring_buffer">Ring buffer</a></li>
103103
</ul>
104104
</li>
105+
<li><a class="el" href="custom_memory_pools.html#buddy_algorithm">Buddy allocation algorithm</a></li>
105106
</ul>
106107
</li>
107108
<li><a class="el" href="defragmentation.html">Defragmentation</a></li>

docs/html/vk__mem__alloc_8h.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -912,14 +912,17 @@ <h2 class="memtitle"><span class="permalink"><a href="#a9a7c45f9c863695d98c83fa5
912912
<tr><th colspan="2">Enumerator</th></tr><tr><td class="fieldname"><a id="a9a7c45f9c863695d98c83fa5ac940fe7a9f1a499508a8edb4e8ba40aa0290a3d2"></a>VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT&#160;</td><td class="fielddoc"><p>Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. </p>
913913
<p>This is an optional optimization flag.</p>
914914
<p>If you always allocate using <a class="el" href="vk__mem__alloc_8h.html#ac72ee55598617e8eecca384e746bab51">vmaCreateBuffer()</a>, <a class="el" href="vk__mem__alloc_8h.html#a02a94f25679275851a53e82eacbcfc73" title="Function similar to vmaCreateBuffer(). ">vmaCreateImage()</a>, <a class="el" href="vk__mem__alloc_8h.html#a7fdf64415b6c3d83c454f28d2c53df7b">vmaAllocateMemoryForBuffer()</a>, then you don't need to use it because allocator knows exact type of your allocations so it can handle Buffer-Image Granularity in the optimal way.</p>
915-
<p>If you also allocate using <a class="el" href="vk__mem__alloc_8h.html#a0faa3f9e5fb233d29d1e00390650febb" title="Function similar to vmaAllocateMemoryForBuffer(). ">vmaAllocateMemoryForImage()</a> or <a class="el" href="vk__mem__alloc_8h.html#abf28077dbf82d0908b8acbe8ee8dd9b8" title="General purpose memory allocation. ">vmaAllocateMemory()</a>, exact type of such allocations is not known, so allocator must be conservative in handling Buffer-Image Granularity, which can lead to suboptimal allocation (wasted memory). In that case, if you can make sure you always allocate only buffers and linear images or only optimal images out of this pool, use this flag to make allocator disregard Buffer-Image Granularity and so make allocations more optimal. </p>
915+
<p>If you also allocate using <a class="el" href="vk__mem__alloc_8h.html#a0faa3f9e5fb233d29d1e00390650febb" title="Function similar to vmaAllocateMemoryForBuffer(). ">vmaAllocateMemoryForImage()</a> or <a class="el" href="vk__mem__alloc_8h.html#abf28077dbf82d0908b8acbe8ee8dd9b8" title="General purpose memory allocation. ">vmaAllocateMemory()</a>, exact type of such allocations is not known, so allocator must be conservative in handling Buffer-Image Granularity, which can lead to suboptimal allocation (wasted memory). In that case, if you can make sure you always allocate only buffers and linear images or only optimal images out of this pool, use this flag to make allocator disregard Buffer-Image Granularity and so make allocations faster and more optimal. </p>
916916
</td></tr>
917917
<tr><td class="fieldname"><a id="a9a7c45f9c863695d98c83fa5ac940fe7a13c8a444197c67866be9cb05599fc726"></a>VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT&#160;</td><td class="fielddoc"><p>Enables alternative, linear allocation algorithm in this pool. </p>
918918
<p>Specify this flag to enable linear allocation algorithm, which always creates new allocations after last one and doesn't reuse space from allocations freed in between. It trades memory consumption for simplified algorithm and data structure, which has better performance and uses less memory for metadata.</p>
919919
<p>By using this flag, you can achieve behavior of free-at-once, stack, ring buffer, and double stack. For details, see documentation chapter <a class="el" href="custom_memory_pools.html#linear_algorithm">Linear allocation algorithm</a>.</p>
920-
<p>When using this flag, you must specify <a class="el" href="struct_vma_pool_create_info.html#ae41142f2834fcdc82baa4883c187b75c" title="Maximum number of blocks that can be allocated in this pool. Optional. ">VmaPoolCreateInfo::maxBlockCount</a> == 1 (or 0 for default). </p>
920+
<p>When using this flag, you must specify <a class="el" href="struct_vma_pool_create_info.html#ae41142f2834fcdc82baa4883c187b75c" title="Maximum number of blocks that can be allocated in this pool. Optional. ">VmaPoolCreateInfo::maxBlockCount</a> == 1 (or 0 for default).</p>
921+
<p>For more details, see <a class="el" href="custom_memory_pools.html#linear_algorithm">Linear allocation algorithm</a>. </p>
921922
</td></tr>
922-
<tr><td class="fieldname"><a id="a9a7c45f9c863695d98c83fa5ac940fe7a97a0dc38e5161b780594d998d313d35e"></a>VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT&#160;</td><td class="fielddoc"><p>TODO </p>
923+
<tr><td class="fieldname"><a id="a9a7c45f9c863695d98c83fa5ac940fe7a97a0dc38e5161b780594d998d313d35e"></a>VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT&#160;</td><td class="fielddoc"><p>Enables alternative, buddy allocation algorithm in this pool. </p>
924+
<p>It operates on a tree of blocks, each having size that is a power of two and a half of its parent's size. Comparing to default algorithm, this one provides faster allocation and deallocation and decreased external fragmentation, at the expense of more memory wasted (internal fragmentation).</p>
925+
<p>For more details, see <a class="el" href="custom_memory_pools.html#buddy_algorithm">Buddy allocation algorithm</a>. </p>
923926
</td></tr>
924927
<tr><td class="fieldname"><a id="a9a7c45f9c863695d98c83fa5ac940fe7af4d270f8f42517a0f70037ceb6ac1d9c"></a>VMA_POOL_CREATE_ALGORITHM_MASK&#160;</td><td class="fielddoc"><p>Bit mask to extract only <code>ALGORITHM</code> bits from entire set of flags. </p>
925928
</td></tr>
@@ -1635,7 +1638,7 @@ <h2 class="memtitle"><span class="permalink"><a href="#a6aced90fcc7b39882b6654a7
16351638
<p>This function works by moving allocations to different places (different <code>VkDeviceMemory</code> objects and/or different offsets) in order to optimize memory usage. Only allocations that are in pAllocations array can be moved. All other allocations are considered nonmovable in this call. Basic rules:</p>
16361639
<ul>
16371640
<li>Only allocations made in memory types that have <code>VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT</code> and <code>VK_MEMORY_PROPERTY_HOST_COHERENT_BIT</code> flags can be compacted. You may pass other allocations but it makes no sense - these will never be moved.</li>
1638-
<li>Custom pools created with <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a13c8a444197c67866be9cb05599fc726" title="Enables alternative, linear allocation algorithm in this pool. ">VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT</a> flag are not defragmented. Allocations passed to this function that come from such pools are ignored.</li>
1641+
<li>Custom pools created with <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a13c8a444197c67866be9cb05599fc726" title="Enables alternative, linear allocation algorithm in this pool. ">VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT</a> or <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a97a0dc38e5161b780594d998d313d35e" title="Enables alternative, buddy allocation algorithm in this pool. ">VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT</a> flag are not defragmented. Allocations passed to this function that come from such pools are ignored.</li>
16391642
<li>Allocations created with <a class="el" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a3fc311d855c2ff53f1090ef5c722b38f" title="Set this flag if the allocation should have its own memory block. ">VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT</a> or created as dedicated allocations for any other reason are also ignored.</li>
16401643
<li>Both allocations made with or without <a class="el" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a11da372cc3a82931c5e5d6146cd9dd1f" title="Set this flag to use a memory that will be persistently mapped and retrieve pointer to it...">VMA_ALLOCATION_CREATE_MAPPED_BIT</a> flag can be compacted. If not persistently mapped, memory will be mapped temporarily inside this function if needed.</li>
16411644
<li>You must not pass same <a class="el" href="struct_vma_allocation.html" title="Represents single memory allocation. ">VmaAllocation</a> object multiple times in pAllocations array.</li>

docs/html/vk__mem__alloc_8h_source.html

Lines changed: 130 additions & 130 deletions
Large diffs are not rendered by default.

src/Tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4217,7 +4217,7 @@ void Test()
42174217
{
42184218
wprintf(L"TESTING:\n");
42194219

4220-
if(true)
4220+
if(false)
42214221
{
42224222
// # Temporarily insert custom tests here
42234223
// ########################################
@@ -4249,7 +4249,7 @@ void Test()
42494249

42504250
{
42514251
FILE* file;
4252-
fopen_s(&file, "LinearAllocator.csv", "w");
4252+
fopen_s(&file, "Algorithms.csv", "w");
42534253
assert(file != NULL);
42544254
BenchmarkAlgorithms(file);
42554255
fclose(file);

src/VmaUsage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ 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_HEAVY_ASSERT(expr) assert(expr)
19+
//#define VMA_HEAVY_ASSERT(expr) assert(expr)
2020
//#define VMA_USE_STL_CONTAINERS 1
2121
//#define VMA_DEDICATED_ALLOCATION 0
2222
//#define VMA_DEBUG_MARGIN 16

src/vk_mem_alloc.h

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Documentation of all members: vk_mem_alloc.h
6060
- [Stack](@ref linear_algorithm_stack)
6161
- [Double stack](@ref linear_algorithm_double_stack)
6262
- [Ring buffer](@ref linear_algorithm_ring_buffer)
63+
- [Buddy allocation algorithm](@ref buddy_algorithm)
6364
- \subpage defragmentation
6465
- \subpage lost_allocations
6566
- \subpage statistics
@@ -665,6 +666,42 @@ succeeds.
665666
Ring buffer is available only in pools with one memory block -
666667
VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
667668

669+
\section buddy_algorithm Buddy allocation algorithm
670+
671+
There is another allocation algorithm that can be used with custom pools, called
672+
"buddy". Its internal data structure is based on a tree of blocks, each having
673+
size that is a power of two and a half of its parent's size. When you want to
674+
allocate memory of certain size, a free node in the tree is located. If it's too
675+
large, it is recursively split into two halves (called "buddies"). However, if
676+
requested allocation size is not a power of two, the size of a tree node is
677+
aligned up to the nearest power of two and the remaining space is wasted. When
678+
two buddy nodes become free, they are merged back into one larger node.
679+
680+
![Buddy allocator](../gfx/Buddy_allocator.png)
681+
682+
The advantage of buddy allocation algorithm over default algorithm is faster
683+
allocation and deallocation, as well as smaller external fragmentation. The
684+
disadvantage is more wasted space (internal fragmentation).
685+
686+
For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
687+
or other sources that describe this concept in general.
688+
689+
To use buddy allocation algorithm with a custom pool, add flag
690+
#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
691+
#VmaPool object.
692+
693+
Several limitations apply to pools that use buddy algorithm:
694+
695+
- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
696+
Otherwise, only largest power of two smaller than the size is used for
697+
allocations. The remaining space always stays unused.
698+
- [Margins](@ref debugging_memory_usage_margins) and
699+
[corruption detection](@ref debugging_memory_usage_corruption_detection)
700+
don't work in such pools.
701+
- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
702+
use them, but they never become lost. Support may be added in the future.
703+
- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
704+
such pool.
668705

669706
\page defragmentation Defragmentation
670707

@@ -987,6 +1024,7 @@ allocations, which have their own memory block of specific size.
9871024
It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
9881025
or those automatically decided to put into dedicated allocations, e.g. due to its
9891026
large size or recommended by VK_KHR_dedicated_allocation extension.
1027+
Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
9901028

9911029
Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
9921030

@@ -1981,10 +2019,20 @@ typedef enum VmaPoolCreateFlagBits {
19812019
\ref linear_algorithm.
19822020

19832021
When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2022+
2023+
For more details, see [Linear allocation algorithm](@ref linear_algorithm).
19842024
*/
19852025
VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
19862026

1987-
/** TODO */
2027+
/** \brief Enables alternative, buddy allocation algorithm in this pool.
2028+
2029+
It operates on a tree of blocks, each having size that is a power of two and
2030+
a half of its parent's size. Comparing to default algorithm, this one provides
2031+
faster allocation and deallocation and decreased external fragmentation,
2032+
at the expense of more memory wasted (internal fragmentation).
2033+
2034+
For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2035+
*/
19882036
VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
19892037

19902038
/** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
@@ -2446,9 +2494,9 @@ allocations are considered nonmovable in this call. Basic rules:
24462494
`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
24472495
flags can be compacted. You may pass other allocations but it makes no sense -
24482496
these will never be moved.
2449-
- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag are not
2450-
defragmented. Allocations passed to this function that come from such pools
2451-
are ignored.
2497+
- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2498+
#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2499+
passed to this function that come from such pools are ignored.
24522500
- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
24532501
created as dedicated allocations for any other reason are also ignored.
24542502
- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT

0 commit comments

Comments
 (0)