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

Flat Memory Pool #572

Merged
merged 7 commits into from
Dec 4, 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
@@ -0,0 +1,150 @@
#pragma once

#if ! JUCE_TEENSY

#if JUCE_WINDOWS
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#endif

namespace chowdsp
{
/**
* A "flat" memory pool that reserves a ton of virtual memory,
* and then commits pages of that memory as needed.
*/
struct FlatMemoryPool
{
std::byte* current_pointer {};
std::byte* memory_base {};
std::byte* first_uncommitted_page {};
std::byte* address_limit {};
const size_t page_size { get_page_size() };

void* allocate_bytes (size_t num_bytes, size_t alignment = 8)
{
auto* p = juce::snapPointerToAlignment (current_pointer, alignment);
const auto end = p + num_bytes;

if (end > first_uncommitted_page)
{
if (end > address_limit)
{
// The memory pool is full!
jassertfalse;
return nullptr;

Check warning on line 39 in modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h#L38-L39

Added lines #L38 - L39 were not covered by tests
}
else
{
extend_committed_pages (end);
}
}

current_pointer = end;
return p;
}

[[nodiscard]] size_t get_bytes_used() const noexcept
{
return static_cast<size_t> (current_pointer - memory_base);
}

void clear()
{
if (memory_base == nullptr)
return;

Check warning on line 59 in modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h#L59

Added line #L59 was not covered by tests

#if ARENA_ALLOCATOR_DEBUG
memset (memory_base, 0xcc, static_cast<size_t> (current_pointer - memory_base));
#endif

current_pointer = memory_base;
}

struct Frame
{
FlatMemoryPool& pool;
std::byte* pointer_at_start;

explicit Frame (FlatMemoryPool& frame_pool)
: pool { frame_pool },
pointer_at_start { pool.current_pointer }
{
}

~Frame()
{
pool.current_pointer = pointer_at_start;
}
};

/** Creates a frame for this allocator */
auto create_frame()
{
return Frame { *this };
}

void init (size_t reserve_bytes = 1 << 28)
{
const auto reserve_padded = page_size * ((reserve_bytes + page_size - 1) / page_size);

#if JUCE_WINDOWS
memory_base = static_cast<std::byte*> (VirtualAlloc (nullptr, reserve_padded, MEM_RESERVE, PAGE_READWRITE));
#else
memory_base = static_cast<std::byte*> (mmap (nullptr, reserve_padded, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0));
jassert (memory_base != nullptr);
#endif

current_pointer = memory_base;
first_uncommitted_page = memory_base;
address_limit = memory_base + reserve_padded;
}

void deinit()
{
if (memory_base == nullptr)
return;

Check warning on line 110 in modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h

View check run for this annotation

Codecov / codecov/patch

modules/common/chowdsp_data_structures/Allocators/chowdsp_FlatMemoryPool.h#L110

Added line #L110 was not covered by tests

#if JUCE_WINDOWS
VirtualFree (memory_base, 0, MEM_RELEASE);
#else
munmap (memory_base, static_cast<size_t> (address_limit - memory_base));
#endif

memory_base = nullptr;
}

private:
void extend_committed_pages (std::byte* end)
{
jassert (end - first_uncommitted_page >= 0);
#if JUCE_WINDOWS
const auto size = juce::snapPointerToAlignment (end, page_size) - first_uncommitted_page;
VirtualAlloc (first_uncommitted_page, static_cast<size_t> (size), MEM_COMMIT, PAGE_READWRITE);
first_uncommitted_page += size;
#else
first_uncommitted_page = juce::snapPointerToAlignment (end, page_size);
#endif
}

#if JUCE_WINDOWS
static size_t get_page_size()
{
SYSTEM_INFO systemInfo;
GetNativeSystemInfo (&systemInfo);
return static_cast<size_t> (systemInfo.dwPageSize);
}
#else
static size_t get_page_size()
{
return static_cast<size_t> (sysconf (_SC_PAGESIZE));
}
#endif
};
} // namespace chowdsp

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@

namespace chowdsp::arena
{
/**
* Allocates space for some number of objects of type T
* The returned memory will be un-initialized, so be sure to clear it manually if needed.
*/
template <typename T, typename IntType, typename Arena>
T* allocate (Arena& arena, IntType num_Ts, size_t alignment = alignof (T)) noexcept
{
return static_cast<T*> (arena.allocate_bytes ((size_t) num_Ts * sizeof (T), alignment));
}

template <typename T, typename I, typename Arena>
nonstd::span<T> make_span (Arena& arena, I size, size_t alignment = alignof (T))
{
return { arena.template allocate<T> (size, alignment), static_cast<size_t> (size) };
return { allocate<T> (arena, size, alignment), static_cast<size_t> (size) };
}

template <typename Arena>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ BEGIN_JUCE_MODULE_DECLARATION
#include "Allocators/chowdsp_STLArenaAllocator.h"
#include "Helpers/chowdsp_ArenaHelpers.h"

#include "Allocators/chowdsp_FlatMemoryPool.h"
#include "Allocators/chowdsp_PoolAllocator.h"

#include "Structures/chowdsp_BucketArray.h"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ target_sources(chowdsp_data_structures_test
PackedPointerTest.cpp
ChunkListTest.cpp
FixedSizeFunctionTest.cpp
FlatMemoryPoolTest.cpp
)

target_compile_features(chowdsp_data_structures_test PRIVATE cxx_std_20)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <CatchUtils.h>
#include <chowdsp_data_structures/chowdsp_data_structures.h>

TEST_CASE ("Flat Memory Pool Test", "[common][data-structures]")
{
chowdsp::FlatMemoryPool allocator {};
allocator.init();

SECTION ("Basic Usage")
{
// allocate doubles
double* some_doubles;
{
some_doubles = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_doubles != nullptr);
REQUIRE (allocator.get_bytes_used() == 80);

auto* some_more_doubles = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_more_doubles != nullptr);
REQUIRE (some_more_doubles == some_doubles + 10);
REQUIRE (allocator.get_bytes_used() == 160);
}

// clear allocator
allocator.clear();

// re-allocate doubles
{
auto* some_doubles2 = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_doubles2 != nullptr);
REQUIRE (some_doubles2 == some_doubles);
REQUIRE (allocator.get_bytes_used() == 80);

auto* some_more_doubles = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_more_doubles != nullptr);
REQUIRE (some_more_doubles == some_doubles + 10);
REQUIRE (allocator.get_bytes_used() == 160);
}
}

SECTION ("Overfull Allocation")
{
REQUIRE (chowdsp::arena::allocate<double> (allocator, 20'000) != nullptr);
REQUIRE (chowdsp::arena::allocate<double> (allocator, 200) != nullptr);
REQUIRE (chowdsp::arena::allocate<double> (allocator, 2'000'000) != nullptr);
REQUIRE (allocator.get_bytes_used() == 8 * 2'020'200);
allocator.clear();
REQUIRE (allocator.get_bytes_used() == 0);
}

SECTION ("Usage with Frame")
{
{
const auto frame = allocator.create_frame();

auto* some_doubles = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_doubles != nullptr);
REQUIRE (allocator.get_bytes_used() == 80);

auto* some_more_doubles = chowdsp::arena::allocate<double> (allocator, 10);
REQUIRE (some_more_doubles != nullptr);
REQUIRE (allocator.get_bytes_used() == 160);
}

REQUIRE (allocator.get_bytes_used() == 0);

allocator.deinit();
REQUIRE (allocator.memory_base == nullptr);
}
}
Loading