Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chained arena allocator: support allocations larger than the chunk size #543

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

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

Expand All @@ -35,8 +36,9 @@
*/
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 @@
*/
void clear() noexcept
{
free_extra_allocs();
current_arena = arena_list.head;
get_current_arena().clear();
}
Expand All @@ -59,8 +62,16 @@
{
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;

Check warning on line 67 in modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h#L65-L67

Added lines #L65 - L67 were not covered by tests
if (extra_alloc_list != nullptr)
extra_alloc->next = extra_alloc_list;

Check warning on line 69 in modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h#L69

Added line #L69 was not covered by tests
else
extra_alloc->next = nullptr;
extra_alloc_list = extra_alloc;

Check warning on line 72 in modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h#L71-L72

Added lines #L71 - L72 were not covered by tests

return extra_alloc->ptr;

Check warning on line 74 in modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h#L74

Added line #L74 was not covered by tests
}

auto pointer = get_current_arena().allocate_bytes (num_bytes, alignment);
Expand Down Expand Up @@ -119,6 +130,10 @@
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 @@
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 @@

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 @@
}
}

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

Check warning on line 276 in modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h#L276

Added line #L276 was not covered by tests
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 @@
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);
}
}
Loading