Custom Allocators and Polymorphic Memory Resources (std::pmr)
Smart Pointers and Memory Management

8.5 Custom Allocators and Polymorphic Memory Resources (std::pmr)

In latency critical systems (such as high frequency trading algorithms or game loop renderers), making dynamic heap allocations during runtime is strictly forbidden. C++17 solved this by introducing Polymorphic Memory Resources (std::pmr), allowing developers to specify custom allocators at runtime.

Let's look at placement new, arena allocators, and the PMR standard library classes.

Placement New: Constructing in Place

Before looking at PMR, we must understand how to construct an object in pre allocated memory. Standard new allocates memory and calls the constructor. Placement New bypasses allocation entirely, constructing the object at a specific memory address you provide:

#include <iostream>
#include <new> // Required for placement new

struct Player {
    int score;
    Player(int s) : score{s} {
        std::cout << "Player constructed!\n";
    }
};

int main() {
    char buffer[sizeof(Player)]; // Stack buffer (pre allocated memory)

    // Placement new: Construct Player directly inside our stack buffer!
    Player* playerPtr = new (buffer) Player{100};

    std::cout << "Score: " << playerPtr->score << '\n';

    // Destructor must be called manually since there is no heap delete!
    playerPtr->~Player();
    return 0;
}
Constructing objects using placement new.

The PMR Monotonic Buffer Resource (Memory Arena)

A Monotonic Buffer Resource (often called an Arena Allocator) is a custom memory pool resource. It allocates a fixed buffer (on the stack or heap) once at startup. When you add elements to a container, it constructs them inside this buffer, increasing a pointer offset.

Allocating from an arena is incredibly fast because it is a simple pointer shift (taking less than a nanosecond) and avoids all OS allocator locks. When the arena resource goes out of scope, the entire buffer is cleared at once:

#include <iostream>
#include <vector>
#include <memory_resource> // Required for std::pmr containers

int main() {
    // Allocate a 500 byte buffer on the stack
    char buffer[500];

    // Create a PMR monotonic buffer resource wrapping our stack buffer
    std::pmr::monotonic_buffer_resource pool{buffer, sizeof(buffer)};

    // Create a vector that allocates its elements directly inside our stack pool!
    std::pmr::vector<int> numbers{&pool};

    // Pushing elements does zero heap allocations! Blazing fast stack shifts.
    for (int i{0}; i < 100; ++i) {
        numbers.push_back(i);
    }
    return 0;
}
Allocating vectors inside a stack arena resource.

* The Bookcase Metaphor: Storing variables with the default allocator is like renting a new storage locker every time you buy a book, then driving to return the key when done. Using an arena allocator is like buying a large bookcase once at startup. Placing books on the shelf is instant, and when you move houses, you just throw the whole bookcase away at once.

Finished reading this lesson?