Skip to content

Commit

Permalink
Chained arena allocator: support allocations larger than the chunk si…
Browse files Browse the repository at this point in the history
…ze (#543)
  • Loading branch information
jatinchowdhury18 authored Jun 26, 2024
1 parent a9bd945 commit 38f4fa5
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ChainedArenaAllocator

~ChainedArenaAllocator()
{
free_extra_allocs();
free_arenas();
}

Expand All @@ -35,8 +36,9 @@ class ChainedArenaAllocator
*/
void reset (size_t head_arena_size_bytes)
{
arena_size_bytes = head_arena_size_bytes;
arena_size_bytes = std::max (head_arena_size_bytes, (size_t) 32);

free_extra_allocs();
free_arenas();

current_arena = bootstrap_arena (arena_size_bytes);
Expand All @@ -50,6 +52,7 @@ class ChainedArenaAllocator
*/
void clear() noexcept
{
free_extra_allocs();
current_arena = arena_list.head;
get_current_arena().clear();
}
Expand All @@ -59,8 +62,16 @@ class ChainedArenaAllocator
{
if (num_bytes > arena_size_bytes)
{
jassertfalse;
return nullptr;
auto* extra_alloc = allocate<ExtraAlloc> (1);
extra_alloc->ptr = aligned_alloc (std::max (alignment, (size_t) 8), num_bytes);
extra_alloc->size = num_bytes;
if (extra_alloc_list != nullptr)
extra_alloc->next = extra_alloc_list;
else
extra_alloc->next = nullptr;
extra_alloc_list = extra_alloc;

return extra_alloc->ptr;
}

auto pointer = get_current_arena().allocate_bytes (num_bytes, alignment);
Expand Down Expand Up @@ -119,6 +130,10 @@ class ChainedArenaAllocator
for (auto* arena = arena_list.head; arena != current_arena; arena = arena->next)
bytes_count += arena->get_bytes_used();
bytes_count += current_arena->get_bytes_used();

for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next)
bytes_count += extra_alloc->size;

return bytes_count;
}

Expand All @@ -131,25 +146,29 @@ class ChainedArenaAllocator
size_t bytes_count = 0;
for (auto* arena = arena_list.head; arena != nullptr; arena = arena->next)
bytes_count += arena->get_total_num_bytes();
for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next)
bytes_count += extra_alloc->size;
return bytes_count;
}

/** Merges another allocator into this one, and invalidates the other allocator. */
void merge (ChainedArenaAllocator& allocator_to_merge)
{
if (allocator_to_merge.arena_list.count == 0)
if (allocator_to_merge.arena_list.count == 0 && allocator_to_merge.extra_alloc_list == nullptr)
return; // no work to do!

// both arenas must have the same head size!
jassert (arena_size_bytes == 0 || arena_size_bytes == allocator_to_merge.arena_size_bytes);

// if our arena is empty, just make this arena into the other arena!
if (arena_list.count == 0)
if (arena_list.count == 0 && extra_alloc_list == nullptr)
{
current_arena = allocator_to_merge.current_arena;
arena_list = allocator_to_merge.arena_list;
extra_alloc_list = allocator_to_merge.extra_alloc_list;
allocator_to_merge.current_arena = nullptr;
allocator_to_merge.arena_list = {};
allocator_to_merge.extra_alloc_list = nullptr;
return;
}

Expand All @@ -172,8 +191,18 @@ class ChainedArenaAllocator

arena_list.count += arena_add_count;

if (allocator_to_merge.extra_alloc_list != nullptr)
{
ExtraAlloc* end_of_list = allocator_to_merge.extra_alloc_list;
while (end_of_list->next != nullptr)
end_of_list = end_of_list->next;
end_of_list->next = extra_alloc_list;
extra_alloc_list = allocator_to_merge.extra_alloc_list;
}

allocator_to_merge.current_arena = nullptr;
allocator_to_merge.arena_list = {};
allocator_to_merge.extra_alloc_list = nullptr;
allocator_to_merge = {};
}

Expand Down Expand Up @@ -241,6 +270,13 @@ class ChainedArenaAllocator
}
}

void free_extra_allocs()
{
for (auto* extra_alloc = extra_alloc_list; extra_alloc != nullptr; extra_alloc = extra_alloc->next)
aligned_free (extra_alloc->ptr);
extra_alloc_list = nullptr;
}

static ArenaNode* bootstrap_arena (size_t num_bytes)
{
static constexpr size_t arena_alignment = 64;
Expand Down Expand Up @@ -273,5 +309,13 @@ class ChainedArenaAllocator
ArenaList arena_list {};
ArenaNode* current_arena {};
size_t arena_size_bytes = 0;

struct ExtraAlloc
{
void* ptr = nullptr;
size_t size {};
ExtraAlloc* next = nullptr;
};
ExtraAlloc* extra_alloc_list = nullptr;
};
} // namespace chowdsp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]")
REQUIRE (allocator.get_total_bytes_used() == 160);
}

// overfull allocation
REQUIRE (allocator.template allocate<double> (200) == nullptr);

// clear allocator
allocator.clear();
REQUIRE (allocator.get_arena_count() == 2);
Expand All @@ -47,6 +44,15 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]")
}
}

SECTION ("Overful Allocation")
{
REQUIRE (allocator.template allocate<double> (200) != nullptr);
REQUIRE (allocator.template allocate<double> (200) != nullptr);
REQUIRE (allocator.get_total_bytes_used() == 3200 + 2 * 24);
REQUIRE (allocator.get_total_bytes() == 3200 + 144);
allocator.clear();
}

SECTION ("Usage with Frame")
{
{
Expand Down Expand Up @@ -89,6 +95,8 @@ TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]")
merge_alloc.merge (alloc1);
alloc2.allocate<float> (24);
alloc2.allocate<float> (24);
alloc2.allocate<float> (48);
alloc2.allocate<float> (48);
merge_alloc.merge (alloc2);
merge_alloc.merge (alloc3);
REQUIRE (merge_alloc.get_arena_count() == arena_count);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

TEST_CASE ("Custom Formatting Test", "[common][logs]")
{
chowdsp::ArenaAllocator<> arena { 2048 };
chowdsp::ChainedArenaAllocator arena { 2048 };

SECTION ("juce::String")
{
Expand All @@ -18,4 +18,11 @@ TEST_CASE ("Custom Formatting Test", "[common][logs]")
const auto format_result = chowdsp::format (arena, "{}", nonstd::span { vec });
REQUIRE (format_result == "{0,1,2,3,4}");
}

SECTION ("Large String")
{
std::string large_string { "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ipsum enim, bibendum venenatis sollicitudin eget, pretium at sem. Vivamus efficitur risus eget nulla tempus, eget lacinia orci dictum. Sed egestas nulla accumsan sodales finibus. Phasellus faucibus, sapien at aliquam gravida, turpis mi pellentesque mauris, et bibendum purus turpis sit amet elit. In in metus ac quam dictum sodales. Integer odio ex, mattis ac pulvinar vitae, congue vitae tortor. Duis porta vehicula consequat. Nullam pellentesque malesuada massa, id fermentum metus ultrices condimentum. Quisque felis mauris, vulputate nec luctus sed, suscipit in justo. Quisque et porta est. In in congue arcu. Donec posuere rutrum sapien, nec efficitur velit ultricies id. Vivamus eget sollicitudin leo. Donec accumsan placerat risus, eget congue enim egestas sit amet. Aliquam erat volutpat. Ut vitae dignissim elit. Integer dignissim id leo eu semper. Nullam pulvinar ac eros et hendrerit. Maecenas imperdiet, odio sit amet cursus maximus, urna augue tempus orci, fermentum porta magna sem id tortor. Sed consectetur, arcu quis placerat eleifend, augue dolor egestas tellus, in tempor eros ante quis urna. Phasellus id eleifend justo, vitae viverra tortor. Phasellus tincidunt porttitor auctor. Fusce a velit quis orci ullamcorper blandit sit amet vel arcu. Ut venenatis mauris ac augue scelerisque, sit amet malesuada lacus aliquam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed nec magna at ipsum suscipit eleifend sit amet ac sapien. Nunc ut metus erat. Suspendisse nisl mauris, accumsan sed scelerisque et, mattis eget sem. Aliquam pulvinar metus a scelerisque vulputate. Etiam non risus risus. Sed ornare nunc fringilla consequat commodo. Nam vitae egestas enim. Duis pretium leo et felis aliquam, ullamcorper pellentesque neque congue. Vestibulum posuere massa vel purus efficitur accumsan. Integer ut metus eget elit viverra blandit. Nulla at tincidunt justo, a ornare nunc. Pellentesque scelerisque eros vitae magna posuere volutpat. Pellentesque euismod nunc vitae odio aliquam, non gravida tellus dignissim. Duis semper eu ligula at sollicitudin. Nullam euismod, diam et mollis consectetur, mauris ex facilisis dolor, nec fringilla massa nulla a dui. Curabitur lacus purus, bibendum ac justo vestibulum, tempus gravida massa. Donec quis condimentum quam. Morbi eleifend mi lectus, quis convallis ante vestibulum eget. Praesent eu metus non mauris scelerisque malesuada. Vivamus malesuada elit ac lacus fermentum sodales. Nullam blandit leo nulla, et condimentum velit bibendum pellentesque. Fusce felis risus, rhoncus eu tempus sed, euismod ut eros. Praesent sagittis placerat nisl nec aliquet. Fusce id eros diam. Nullam in tincidunt lectus. Vestibulum at finibus sem. Suspendisse eleifend mauris nec nulla ornare lacinia in vel felis. Sed finibus tempor pharetra. Aliquam efficitur aliquam suscipit. Phasellus commodo euismod posuere. Phasellus pharetra justo quam, eu auctor orci laoreet quis." };
const auto format_result = chowdsp::format (arena, "{}", large_string);
REQUIRE (format_result == large_string);
}
}

0 comments on commit 38f4fa5

Please sign in to comment.