diff --git a/modules/common/chowdsp_core/Memory/chowdsp_AlignedAlloc.h b/modules/common/chowdsp_core/Memory/chowdsp_AlignedAlloc.h new file mode 100644 index 000000000..2bb9825b4 --- /dev/null +++ b/modules/common/chowdsp_core/Memory/chowdsp_AlignedAlloc.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace chowdsp +{ +#if JUCE_TEENSY + +/** MSVC-compatible implementation of aligned_alloc */ +[[nodiscard]] inline void* aligned_alloc ([[maybe_unused]] size_t alignment, size_t size) +{ + // @TODO + return malloc (size + alignment); +} + +/** MSVC-compatible implementation of aligned_free */ +inline void aligned_free (void* data) +{ + free (data); +} + +#elif defined(_MSC_VER) + +/** MSVC-compatible implementation of aligned_alloc */ +[[nodiscard]] inline void* aligned_alloc (size_t alignment, size_t size) +{ + return _aligned_malloc (size, alignment); +} + +/** MSVC-compatible implementation of aligned_free */ +inline void aligned_free (void* data) +{ + _aligned_free (data); +} + +#else + +/** MSVC-compatible implementation of aligned_alloc */ +[[nodiscard]] inline void* aligned_alloc (size_t alignment, size_t size) +{ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" +#endif + + return std::aligned_alloc (alignment, size); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} + +/** MSVC-compatible implementation of aligned_free */ +inline void aligned_free (void* data) +{ + std::free (data); +} + +#endif +} // namespace chowdsp diff --git a/modules/common/chowdsp_core/chowdsp_core.h b/modules/common/chowdsp_core/chowdsp_core.h index a62acc1e2..bfc1bb101 100644 --- a/modules/common/chowdsp_core/chowdsp_core.h +++ b/modules/common/chowdsp_core/chowdsp_core.h @@ -99,6 +99,7 @@ namespace experimental #include "Functional/chowdsp_Bindings.h" #include "Functional/chowdsp_EndOfScopeAction.h" +#include "Memory/chowdsp_AlignedAlloc.h" #include "Memory/chowdsp_MemoryAliasing.h" #include "Types/chowdsp_TypeHasCheckers.h" #include "Types/chowdsp_TypeTraits.h" diff --git a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ArenaAllocator.h b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ArenaAllocator.h index 3b9e09377..0dd66f8dd 100644 --- a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ArenaAllocator.h +++ b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ArenaAllocator.h @@ -17,6 +17,15 @@ class ArenaAllocator reset (size_in_bytes); } + template >>* = nullptr> + ArenaAllocator (ArenaAllocator& other) // NOLINT + : raw_data { other.raw_data }, + bytes_used { other.bytes_used } + { + } + ArenaAllocator (const ArenaAllocator&) = delete; ArenaAllocator& operator= (const ArenaAllocator&) = delete; @@ -41,9 +50,18 @@ class ArenaAllocator bytes_used = 0; } - /** Returns the number of bytes currently being used */ + /** Returns the number of bytes currently being used. */ [[nodiscard]] size_t get_bytes_used() const noexcept { return bytes_used; } + /** Returns the total number of bytes the allocator manages. */ + [[nodiscard]] size_t get_total_num_bytes() const noexcept { return raw_data.size(); } + + /** Returns a reference to the allocator's internal memory resource. */ + [[nodiscard]] auto& get_memory_resource() { return raw_data; } + + /** Returns a reference to the allocator's internal memory resource. */ + [[nodiscard]] const auto& get_memory_resource() const { return raw_data; } + /** * Allocates a given number of bytes. * The returned memory will be un-initialized, so be sure to clear it manually if needed. @@ -99,9 +117,6 @@ class ArenaAllocator const size_t bytes_used_at_start; }; - /** Deprecated alias for arena allocator frame */ - using ArenaAllocatorFrame [[deprecated]] = Frame; - /** Creates a frame for this allocator */ auto create_frame() { @@ -110,8 +125,15 @@ class ArenaAllocator private: MemoryResourceType raw_data {}; - size_t bytes_used = 0; + size_t bytes_used_internal = 0; + size_t& bytes_used = bytes_used_internal; + + template + friend class ArenaAllocator; CHOWDSP_CHECK_HAS_METHOD (MemoryResourceTypeHasResize, resize, std::declval()) }; + +/** Convenient alias for a "non-owning" arena. */ +using ArenaAllocatorView = ArenaAllocator>; } // namespace chowdsp diff --git a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h index 4db796632..ef550aed3 100644 --- a/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h +++ b/modules/common/chowdsp_data_structures/Allocators/chowdsp_ChainedArenaAllocator.h @@ -4,17 +4,10 @@ namespace chowdsp { -template > class ChainedArenaAllocator { public: - using BaseArena = ArenaAllocator; - - ChainedArenaAllocator() - { - if (MemoryResourceType test_mrt {}; test_mrt.size() > 0) - reset (test_mrt.size()); - } + ChainedArenaAllocator() = default; /** Constructs the arena with an initial allocated size. */ explicit ChainedArenaAllocator (size_t size_in_bytes) @@ -28,6 +21,11 @@ class ChainedArenaAllocator ChainedArenaAllocator (ChainedArenaAllocator&&) noexcept = default; ChainedArenaAllocator& operator= (ChainedArenaAllocator&&) noexcept = default; + ~ChainedArenaAllocator() + { + free_arenas(); + } + /** * Selects the size used for each of the internal arena allocators, * and "resets" the allocator back to a single arena with that size. @@ -36,10 +34,12 @@ class ChainedArenaAllocator { arena_size_bytes = head_arena_size_bytes; + free_arenas(); arenas.clear(); arenas.emplace_front (arena_size_bytes); current_arena = arenas.begin(); + allocate_current_arena (arena_size_bytes); arena_count = 1; } @@ -85,7 +85,7 @@ class ChainedArenaAllocator } /** Returns the arena currently being used */ - BaseArena& get_current_arena() + ArenaAllocatorView& get_current_arena() { jassert (current_arena != arenas.end()); return *current_arena; @@ -110,7 +110,7 @@ class ChainedArenaAllocator * Note that due to the design of the allocator, not all * of the available bytes may be used at any given time. * As such, the allocator's "load factor" can be computed - * as: get_total_bytes_used() / (get_arena_count() * arena_size_bytes) + * as: get_total_bytes_used() / get_total_bytes() */ [[nodiscard]] size_t get_total_bytes_used() const noexcept { @@ -121,6 +121,18 @@ class ChainedArenaAllocator return bytes_count; } + /** + * Returns the total number of bytes being managed by + * this allocator. + */ + [[nodiscard]] size_t get_total_bytes() const noexcept + { + size_t bytes_count = 0; + for (const auto& arena : arenas) + bytes_count += arena.get_total_num_bytes(); + return bytes_count; + } + struct Frame { explicit Frame (ChainedArenaAllocator& allocator) @@ -136,8 +148,8 @@ class ChainedArenaAllocator } ChainedArenaAllocator& alloc; - const typename std::forward_list::iterator arena_at_start; - typename BaseArena::Frame arena_frame; + const std::forward_list::iterator arena_at_start; + ArenaAllocatorView::Frame arena_frame; }; /** Creates a frame for this allocator */ @@ -149,14 +161,14 @@ class ChainedArenaAllocator private: void add_arena_to_chain() { - const auto prev_arena = current_arena; - current_arena++; + const auto prev_arena = current_arena++; // if we've reached the end of the list, then we need to // add a new arena to the chain (starting after the previous arena) if (current_arena == arenas.end()) { - current_arena = arenas.emplace_after (prev_arena, arena_size_bytes); + current_arena = arenas.emplace_after (prev_arena); + allocate_current_arena (arena_size_bytes); arena_count++; return; } @@ -164,8 +176,24 @@ class ChainedArenaAllocator get_current_arena().clear(); } - std::forward_list arenas {}; - typename std::forward_list::iterator current_arena {}; + void allocate_current_arena (size_t num_bytes) + { + static constexpr size_t arena_alignment = 64; + const auto num_bytes_padded = arena_alignment * ((num_bytes + arena_alignment - 1) / arena_alignment); + current_arena->get_memory_resource() = { + static_cast (aligned_alloc (arena_alignment, num_bytes_padded)), + num_bytes_padded, + }; + } + + void free_arenas() + { + for (auto& arena : arenas) + aligned_free (arena.get_memory_resource().data()); + } + + std::forward_list arenas {}; + std::forward_list::iterator current_arena {}; size_t arena_size_bytes = 0; size_t arena_count = 0; }; diff --git a/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.h b/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.h index 27644fdc6..d82a8da6c 100644 --- a/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.h +++ b/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.h @@ -79,10 +79,9 @@ class SmoothedBufferValue */ void process (int numSamples); - template - void process (int numSamples, ArenaAllocator& alloc) + void process (int numSamples, ArenaAllocatorView alloc) { - bufferData = alloc.template allocate (numSamples, bufferAlignment); + bufferData = alloc.allocate (numSamples, bufferAlignment); jassert (bufferData != nullptr); // arena allocator is out of memory! process (numSamples); } @@ -93,10 +92,9 @@ class SmoothedBufferValue */ void process (FloatType value, int numSamples); - template - void process (FloatType value, int numSamples, ArenaAllocator& alloc) + void process (FloatType value, int numSamples, ArenaAllocatorView alloc) { - bufferData = alloc.template allocate (numSamples, bufferAlignment); + bufferData = alloc.allocate (numSamples, bufferAlignment); jassert (bufferData != nullptr); // arena allocator is out of memory! process (value, numSamples); } diff --git a/modules/gui/chowdsp_gui/Helpers/chowdsp_ComponentArena.h b/modules/gui/chowdsp_gui/Helpers/chowdsp_ComponentArena.h index 59a0dc407..f7992c24b 100644 --- a/modules/gui/chowdsp_gui/Helpers/chowdsp_ComponentArena.h +++ b/modules/gui/chowdsp_gui/Helpers/chowdsp_ComponentArena.h @@ -59,7 +59,7 @@ class ComponentArena allocator.clear(); } - ChainedArenaAllocator> allocator {}; + ChainedArenaAllocator allocator { arena_chunk_size_bytes }; private: SmallVector, approx_component_count> component_list {}; diff --git a/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp b/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp index e8d4e63db..552d04249 100644 --- a/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/ChainedArenaAllocatorTest.cpp @@ -1,28 +1,20 @@ #include #include -TEMPLATE_TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]", (std::vector), (std::array) ) +TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]") { - using MemoryResourceType = TestType; - - auto allocator = [] - { - if constexpr (std::is_same_v>) - return chowdsp::ChainedArenaAllocator { 100 }; - else - return chowdsp::ChainedArenaAllocator {}; - }(); + chowdsp::ChainedArenaAllocator allocator { 100 }; SECTION ("Basic Usage") { // allocate doubles { - auto* some_doubles = allocator.template allocate (10); + auto* some_doubles = allocator.allocate (10); REQUIRE ((void*) some_doubles == allocator.template data (0)); REQUIRE (allocator.get_arena_count() == 1); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80); - auto* some_more_doubles = allocator.template allocate (10); + auto* some_more_doubles = allocator.allocate (10); REQUIRE (some_more_doubles != nullptr); REQUIRE (allocator.get_arena_count() == 2); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80); @@ -41,12 +33,12 @@ TEMPLATE_TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]", // re-allocate doubles { - auto* some_doubles = allocator.template allocate (10); + auto* some_doubles = allocator.allocate (10); REQUIRE ((void*) some_doubles == allocator.template data (0)); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80); REQUIRE (allocator.get_total_bytes_used() == 80); - auto* some_more_doubles = allocator.template allocate (10); + auto* some_more_doubles = allocator.allocate (10); REQUIRE (some_more_doubles != nullptr); REQUIRE (allocator.get_arena_count() == 2); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80); @@ -60,12 +52,12 @@ TEMPLATE_TEST_CASE ("Chained Arena Allocator Test", "[common][data-structures]", { const auto frame = allocator.create_frame(); - auto* some_doubles = allocator.template allocate (10); + auto* some_doubles = allocator.allocate (10); REQUIRE ((void*) some_doubles == allocator.template data (0)); REQUIRE (allocator.get_arena_count() == 1); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80); - auto* some_more_doubles = allocator.template allocate (10); + auto* some_more_doubles = allocator.allocate (10); REQUIRE (some_more_doubles != nullptr); REQUIRE (allocator.get_arena_count() == 2); REQUIRE (allocator.get_arenas().front().get_bytes_used() == 80);