Memory Management
Amplitude includes a custom memory management system designed for predictable performance, debugging, and integration with game engine memory budgets. This document explains how it works and how to use it effectively.
Why Custom Memory Management?¶
Standard malloc/free have several drawbacks for game audio:
- Unpredictable allocation times: General-purpose allocators can stall during defragmentation.
- No budget tracking: It's hard to know how much memory audio is using.
- Platform differences: Console platforms often require custom allocators.
- Alignment requirements: SIMD operations need specifically aligned buffers.
Amplitude's memory system addresses these issues with pool-based allocation, alignment support, and optional leak tracking.
Memory Pools¶
All allocations are categorized into one of seven pools:
| Pool | Purpose | Typical Lifetime |
|---|---|---|
Engine | Core engine state | Application lifetime |
Amplimix | Mixer buffers and state | Per-frame |
SoundData | Decoded audio data | Sound duration |
Filtering | DSP effect instances | Sound duration |
Codec | Codec internal buffers | File operation |
IO | File system buffers | File operation |
Default | Uncategorized | Varies |
Separating allocations by pool allows:
- Accurate statistics: See exactly which subsystem uses memory.
- Targeted optimization: Optimize the allocator per pool.
- Budget enforcement: Limit memory per subsystem.
Allocation Macros¶
Amplitude provides macros that wrap the memory manager:
// Raw allocation
void* buffer = ampoolmalloc(eMemoryPoolKind_SoundData, 4096);
ampoolfree(eMemoryPoolKind_SoundData, buffer);
// Aligned allocation (for SIMD)
void* aligned = ampoolmalign(eMemoryPoolKind_Amplimix, 4096, 32);
ampoolfree(eMemoryPoolKind_Amplimix, aligned);
// C++ object construction
MyClass* obj = ampoolnew(eMemoryPoolKind_Engine, MyClass, args);
ampooldelete(eMemoryPoolKind_Engine, MyClass, obj);
Smart Pointers¶
Pool-aware smart pointers ensure correct deallocation:
// Unique pointer
AmUniquePtr<MyClass, eMemoryPoolKind_Engine> ptr =
ampoolunique(eMemoryPoolKind_Engine, MyClass, args);
// Shared pointer
AmSharedPtr<MyClass, eMemoryPoolKind_Engine> shared =
AmSharedPtr<MyClass, eMemoryPoolKind_Engine>::Make(args);
Default Allocator¶
The default allocator (DefaultMemoryAllocator) is a simple bucket-based allocator that wraps the platform's malloc. It is suitable for most desktop and mobile use cases.
You can replace it with a custom allocator:
class MyAllocator : public MemoryAllocator
{
public:
void* Malloc(AmSize size, const char* file, AmUInt32 line) override;
void* Malign(AmSize size, AmSize alignment, const char* file, AmUInt32 line) override;
void Free(void* ptr) override;
// ...
};
MemoryManager::Initialize(std::make_shared<MyAllocator>());
Leak Detection¶
When statistics are enabled (the default on debug builds), the memory manager tracks every allocation with its file and line number:
// This allocation is tracked
void* buffer = ampoolmalloc(eMemoryPoolKind_Engine, 1024);
// File: MyGame.cpp, Line: 42
On shutdown, if any allocations remain, you can log them using MemoryManager::InspectMemoryLeaks():
To disable tracking (for release builds), define AM_NO_MEMORY_STATS:
Scoped Allocations¶
For temporary buffers, use ScopedMemoryAllocation:
{
ScopedMemoryAllocation scoped(eMemoryPoolKind_Amplimix, 4096);
void* temp = scoped.GetPointer();
// Process audio...
} // Automatically freed here
This prevents leaks and reduces manual free calls.
Alignment¶
SIMD operations (SSE, AVX, NEON) require aligned memory. Amplitude's AmAlignedReal32Buffer uses ammalign internally:
AmAlignedReal32Buffer buffer;
buffer.Init(1024); // Allocates 1024 floats, 32-byte aligned
// Safe for SIMD
ProcessSIMD(buffer.GetBuffer(), 1024);
Always align audio buffers to at least 16 bytes (32 bytes for AVX).
Best Practices¶
- Always use pool macros: Never use raw
malloc/freefor Amplitude-related allocations. - Choose the right pool: This makes debugging and optimization much easier.
- Use scoped allocations for temporaries: Prevents leaks and simplifies code.
- Check stats in development: Monitor pool usage to catch unexpected growth.
Next Steps¶
- Review the Memory Tuning Guide.
- Explore the MemoryManager API Reference.