C/C++ Arena

Step 5 of 5

Custom allocators with std::pmr

new and malloc are general-purpose: they handle any size, any order of frees, from any thread. That generality costs. An allocation may take a lock, search free lists, or ask the operating system for more memory, and its time varies. Low-latency code (games, trading, audio) often forbids heap allocation in its hot loop entirely, and uses arenas and pools instead:

C++17's polymorphic memory resources (std::pmr, header <memory_resource>) let standard containers use them. A std::pmr::vector<int> is a vector that asks a std::pmr::memory_resource*, chosen at run time, for its memory:

#include <array>
#include <cstddef>
#include <iostream>
#include <memory_resource>
#include <vector>

int main() {
    std::array<std::byte, 4096> buffer;                   // lives on the stack
    std::pmr::monotonic_buffer_resource arena(
        buffer.data(), buffer.size(),
        std::pmr::null_memory_resource());                // no fallback: never touch the heap
    std::pmr::vector<int> squares(&arena);
    squares.reserve(100);
    for (int i = 0; i < 100; i++) squares.push_back(i * i);
    std::pmr::vector<int> firsts(squares.begin(), squares.begin() + 10, &arena);
    std::cout << squares.back() << " " << firsts.size() << "\n";
}                                                         // the arena releases everything at once
9801 10

How it works

Your turn: write CountingResource, a memory resource that passes every request on to an upstream resource (the normal heap by default) and counts: allocations() is the number of allocations so far, and bytes_in_use() is the number of bytes currently allocated and not yet freed. It's a tool for proving that code allocates as little as you think.

Previous: False sharing and alignment