Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Most Malleable Memory Management Method

Most Malleable Memory Management Method

Examples for wanting to manage the memory usage of your program can be to reduce the cost of heap allocations, improve locality of reference, or maybe reduce heap fragmentation.

Regardless of reason, PMR, Polymorphic Memory Resource, is available since C++17, and makes your life much easier.

I will show you...

* Tools and techniques for analysing the memory usage of your program.

* How PMR makes memory management easier.

* How to use PMR with the standard library types.

* How to make your own types use PMR.

* Advice, and pitfalls to avoid, on your quest to improving the memory usage of your program.

Björn Fahller

June 30, 2023
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 1/112

    View Slide

  2. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 2/112
    Most Malleable Memory Management Method
    Björn Fahller

    View Slide

  3. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 3/112
    Most Malleable Memory Management Method

    View Slide

  4. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 4/112
    Most Malleable Memory Management Method

    View Slide

  5. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 5/112
    Most Malleable Memory Management Method

    View Slide

  6. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 6/112
    Most Malleable Memory Management Method

    View Slide

  7. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 7/112
    Most Malleable Memory Management Method
    Björn Fahller

    View Slide

  8. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 8/112
    The heap
    Allows you to...
    – Allocate objects of any size and
    alignment
    – Allocate and deallocate from any
    thread

    Even allocate in one thread and
    deallocate in another
    – Let objects lifetimes vary wildly

    View Slide

  9. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 9/112
    The heap
    Allows you to...
    – Allocate objects of any size and
    alignment
    – Allocate and deallocate from any
    thread

    Even allocate in one thread and
    deallocate in another
    – Let objects lifetimes vary wildly
    Complex
    structure with
    lookup time and
    space overhead

    View Slide

  10. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 10/112
    The heap
    Allows you to...
    – Allocate objects of any size and
    alignment
    – Allocate and deallocate from any
    thread

    Even allocate in one thread and
    deallocate in another
    – Let objects lifetimes vary wildly
    Poor locality
    of reference

    View Slide

  11. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 11/112
    The heap
    Allows you to...
    – Allocate objects of any size and
    alignment
    – Allocate and deallocate from any
    thread

    Even allocate in one thread and
    deallocate in another
    – Let objects lifetimes vary wildly
    Fragmentation may
    make everything
    worse over time

    View Slide

  12. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 12/112
    The heap
    Allows you to...
    – Allocate objects of any size and
    alignment
    – Allocate and deallocate from any
    thread

    Even allocate in one thread and
    deallocate in another
    – Let objects lifetimes vary wildly
    Synchronisation
    overhead

    View Slide

  13. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 13/112
    So you turn to allocators...

    View Slide

  14. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 14/112
    So you turn to allocators...
    https://www.edvardmunch.org/the-scream.jsp

    View Slide

  15. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 15/112
    So you turn to allocators...
    https://www.edvardmunch.org/the-scream.jsp
    But PMR
    allocators suck
    less

    View Slide

  16. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 16/112
    Polymorphic Memory Resource
    “container”
    PMR allocator <>

    View Slide

  17. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 17/112
    Polymorphic Memory Resource
    “container”
    PMR allocator <>
    A “container” has a
    PMR allocator

    View Slide

  18. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 18/112
    Polymorphic Memory Resource
    “container”
    PMR allocator <>
    A “container” has a
    PMR allocator
    A PMR allocator
    references a
    memory resource

    View Slide

  19. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 19/112
    Polymorphic Memory Resource
    “container”
    PMR allocator <>
    A “container” has a
    PMR allocator
    A PMR allocator
    references a
    memory resource
    Standard containers are
    conveniently available under
    namespace std::pmr, e.g.
    std::pmr::vector

    View Slide

  20. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 20/112
    memory_resource
    class std::pmr::memory_resource {
    public:
            void* allocate(size_t bytes, size_t alignment);
            void deallocate(void* addr, size_t bytes, size_t alignment);
            bool is_equal(const memory_resource&) const noexcept;
    private:
            virtual void* do_allocate(size_t bytes, size_t align) = 0;
            virtual void do_deallocate(void* addr, size_t bytes, size_t align) = 0;
            virtual bool do_is_equal(const memory_resource&) const noexcept = 0;
    };

    View Slide

  21. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 21/112
    memory_resource
    class std::pmr::memory_resource {
    public:
            void* allocate(size_t bytes, size_t alignment);
            void deallocate(void* addr, size_t bytes, size_t alignment);
            bool is_equal(const memory_resource&) const noexcept;
    private:
            virtual void* do_allocate(size_t bytes, size_t align) = 0;
            virtual void do_deallocate(void* addr, size_t bytes, size_t align) = 0;
            virtual bool do_is_equal(const memory_resource&) const noexcept = 0;
    };

    Inherit and
    implement these

    View Slide

  22. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 22/112
    memory_resource
    class std::pmr::memory_resource {
    public:
            void* allocate(size_t bytes, size_t alignment);
            void deallocate(void* addr, size_t bytes, size_t alignment);
            bool is_equal(const memory_resource&) const noexcept;
    private:
            virtual void* do_allocate(size_t bytes, size_t align) = 0;
            virtual void do_deallocate(void* addr, size_t bytes, size_t align) = 0;
            virtual bool do_is_equal(const memory_resource&) const noexcept = 0;
    };

    Mildly annoying that
    alignment uses
    size_t and not
    align_val_t
    Inherit and
    implement these

    View Slide

  23. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 23/112
    memory_resource
    class tracing_resource final : public std::pmr::memory_resource {
    public:
        tracing_resource(std::ostream& os) : os_(os) {}
    private:
        void* do_allocate(size_t bytes, size_t align) override    {
            auto addr = operator new (bytes, std::align_val_t(align));
            os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
            return addr;
        }
        void do_deallocate(void* addr, size_t bytes, size_t align) override {
            os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
            operator delete(addr, bytes, std::align_val_t(align));
        }
        bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
            return &other == this;
        }
        std::ostream& os_;
    };

    View Slide

  24. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 24/112
    memory_resource
    class tracing_resource final : public std::pmr::memory_resource {
    public:
        tracing_resource(std::ostream& os) : os_(os) {}
    private:
        void* do_allocate(size_t bytes, size_t align) override    {
            auto addr = operator new (bytes, std::align_val_t(align));
            os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
            return addr;
        }
        void do_deallocate(void* addr, size_t bytes, size_t align) override {
            os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
            operator delete(addr, bytes, std::align_val_t(align));
        }
        bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
            return &other == this;
        }
        std::ostream& os_;
    };

    View Slide

  25. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 25/112
    memory_resource
    class tracing_resource final : public std::pmr::memory_resource {
    public:
        tracing_resource(std::ostream& os) : os_(os) {}
    private:
        void* do_allocate(size_t bytes, size_t align) override    {
            auto addr = operator new (bytes, std::align_val_t(align));
            os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
            return addr;
        }
        void do_deallocate(void* addr, size_t bytes, size_t align) override {
            os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
            operator delete(addr, bytes, std::align_val_t(align));
        }
        bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
            return &other == this;
        }
        std::ostream& os_;
    };

    View Slide

  26. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 26/112
    memory_resource
    class tracing_resource final : public std::pmr::memory_resource {
    public:
        tracing_resource(std::ostream& os) : os_(os) {}
    private:
        void* do_allocate(size_t bytes, size_t align) override    {
            auto addr = operator new (bytes, std::align_val_t(align));
            os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
            return addr;
        }
        void do_deallocate(void* addr, size_t bytes, size_t align) override {
            os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
            operator delete(addr, bytes, std::align_val_t(align));
        }
        bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
            return &other == this;
        }
        std::ostream& os_;
    };

    View Slide

  27. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 27/112
    memory_resource
    class tracing_resource final : public std::pmr::memory_resource {
    public:
        tracing_resource(std::ostream& os) : os_(os) {}
    private:
        void* do_allocate(size_t bytes, size_t align) override    {
            auto addr = operator new (bytes, std::align_val_t(align));
            os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
            return addr;
        }
        void do_deallocate(void* addr, size_t bytes, size_t align) override {
            os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
            operator delete(addr, bytes, std::align_val_t(align));
        }
        bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
            return &other == this;
        }
        std::ostream& os_;
    };

    View Slide

  28. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 28/112
    https://godbolt.org/z/4WvYe1eKj

    View Slide

  29. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 29/112
    Most Malleable Memory Management Method
    It is very important that
    your memory resource
    instance outlives all
    containers that use it.

    View Slide

  30. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 30/112
    Most Malleable Memory Management Method
    It is very important that
    your memory resource
    instance outlives all
    containers that use it.
    It’s very easy to miss using
    a PMR type, and not so
    easy to detect the mistake

    View Slide

  31. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 31/112
    Most Malleable Memory Management Method
    It is very important that
    your memory resource
    instance outlives all
    containers that use it.
    It’s very easy to miss using
    a PMR type, and not so
    easy to detect the mistake
    There are
    tools, though

    View Slide

  32. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 32/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  33. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 33/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  34. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 34/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  35. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 35/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  36. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 36/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  37. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 37/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  38. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 38/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };

    View Slide

  39. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 39/112
    string frequency example
    class histogram {
    public:
        void add(const std::string& word) {
            ++words_[word];
        }
        void print_top(size_t n, std::ostream& os) const {
            using count = std::pair;
            std::vector popular(words_.begin(), words_.end());
            std::ranges::partial_sort(popular,
                                                     
    popular.begin() + n,
    std::greater{},
    &count::second);
            for (const auto& stat : popular | std::ranges::views::take(n)) {
                os << stat.first << '\t' << stat.second << '\n';
            }
        }
    private:
        std::unordered_map words_;
    };
    int main()
    {
    std::string word;
    histogram hist;
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }

    View Slide

  40. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 40/112
    Live Demo

    View Slide

  41. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 41/112
    Most Malleable Memory Management Method
    Nothing surprising here, but good to have a baseline and a tool
    Let’s PMR it!

    View Slide

  42. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 42/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };

    View Slide

  43. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 43/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    PMR-ify the
    container and strings

    View Slide

  44. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 44/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    Get an allocator
    to use

    View Slide

  45. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 45/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    In C++17 it was necessary to
    express a type here, but in
    C++20 onwards, just use the
    diamond

    View Slide

  46. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 46/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    histogram hist;
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }

    View Slide

  47. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 47/112
    Live Demo

    View Slide

  48. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 48/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    histogram hist;
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }

    View Slide

  49. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 49/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    histogram hist;
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }
    Not much new, because a
    default constructed
    polymorphic_allocator
    uses the default
    memory_resource, which is
    new_delete_resource.

    View Slide

  50. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 50/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    histogram hist;
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }
    But we did
    see it being
    used!

    View Slide

  51. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 51/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource

    View Slide

  52. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 52/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    You’ve seen this one.
    Plain operator new
    and operator
    delete.

    View Slide

  53. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 53/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    A resource that always
    fails to allocate. Can be
    useful when testing.

    View Slide

  54. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 54/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource A thread safe pool of
    memory blocks, in
    which allocations are
    simple indexing
    operations into an
    available slot in a
    block.

    View Slide

  55. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 55/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    Same as above, but
    without the overhead of
    thread synchronization.
    Very fast when you
    know allocation and
    deallocation will
    happen in the same
    thread.

    View Slide

  56. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 56/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    Very fast resource,
    where each allocation
    is at a monotonically
    increasing space in the
    available buffer.
    Deallocation only
    happens when the
    resource is destroyed.
    Useful in very local
    scopes.

    View Slide

  57. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 57/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    You can cascade
    memory resources.
    Construct with a
    pointer to an
    upstream resource

    View Slide

  58. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 58/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);

    View Slide

  59. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 59/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    Construct a polymorphic
    allocator using pool.

    View Slide

  60. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 60/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    Constrct a pool
    resource using
    monotonic_buffers.

    View Slide

  61. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 61/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    Construct a
    monotonic buffer
    resource using
    the heap.

    View Slide

  62. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 62/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    Get the
    new_delete_allocator.

    View Slide

  63. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 63/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);

    View Slide

  64. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 64/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    The allocator will
    use the pool to
    allocate memory.

    View Slide

  65. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 65/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    If the pool does not
    have any free memory,
    it allocates more via the
    monotonic buffer.

    View Slide

  66. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 66/112
    Choosing a memory resource
    The header offers:
    std::pmr::new_delete_resource
    std::pmr::null_resource
    std::pmr::synchronized_pool_resource
    std::pmr::unsynchronized_pool_resource
    std::pmr::monotonic_buffer_resource
    void do_work(std::pmr::polymorphic_allocator<> alloc);
    auto heap = std::pmr::new_delete_resource();
    std::pmr::monotonic_buffer_resource monotonic_buffers(heap);
    std::pmr::unsynchronized_pool_resource pool(&monotonic_buffers);
    do_work(&pool);
    If the monotonic
    buffer doesn’t have
    any memory, it gets
    more from the heap

    View Slide

  67. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 67/112
    A slight detour
    class tracing_resource final : public std::pmr::memory_resource {
    public:
    tracing_resource(std::ostream& os) : os_(os) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
    auto addr = operator new (bytes, std::align_val_t(align));
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    operator delete(addr, bytes, std::align_val_t(align));
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    return &other == this;
    }
    std::ostream& os_;
    };

    View Slide

  68. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 68/112
    A slight detour
    class tracing_resource final : public std::pmr::memory_resource {
    public:
    tracing_resource(std::ostream& os) : os_(os) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
    auto addr = operator new (bytes, std::align_val_t(align));
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    operator delete(addr, bytes, std::align_val_t(align));
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    return &other == this;
    }
    std::ostream& os_;
    };
    Maybe going
    directly to the
    heap isn’t such
    a great idea?

    View Slide

  69. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 69/112
    Improving the tracing resource
    class tracing_resource final : public std::pmr::memory_resource
    {
    public:
    tracing_resource(std::ostream& os,
    std::pmr::memory_resource* next = std::pmr::get_default_resource())
    : os_(os), next_(next) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
      auto* addr = next_->allocate(bytes, align);
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    next_->deallocate(addr, bytes, align);
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    auto p = dynamic_cast(&other);
    return p && next_ == p->next_;
    }
    std::ostream& os_;
    std::pmr::memory_resource* next_;
    };

    View Slide

  70. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 70/112
    Improving the tracing resource
    class tracing_resource final : public std::pmr::memory_resource
    {
    public:
    tracing_resource(std::ostream& os,
    std::pmr::memory_resource* next = std::pmr::get_default_resource())
    : os_(os), next_(next) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
      auto* addr = next_->allocate(bytes, align);
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    next_->deallocate(addr, bytes, align);
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    auto p = dynamic_cast(&other);
    return p && next_ == p->next_;
    }
    std::ostream& os_;
    std::pmr::memory_resource* next_;
    };

    View Slide

  71. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 71/112
    Improving the tracing resource
    class tracing_resource final : public std::pmr::memory_resource
    {
    public:
    tracing_resource(std::ostream& os,
    std::pmr::memory_resource* next = std::pmr::get_default_resource())
    : os_(os), next_(next) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
      auto* addr = next_->allocate(bytes, align);
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    next_->deallocate(addr, bytes, align);
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    auto p = dynamic_cast(&other);
    return p && next_ == p->next_;
    }
    std::ostream& os_;
    std::pmr::memory_resource* next_;
    };
    Get an underlying
    memory resource

    View Slide

  72. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 72/112
    Improving the tracing resource
    class tracing_resource final : public std::pmr::memory_resource
    {
    public:
    tracing_resource(std::ostream& os,
    std::pmr::memory_resource* next = std::pmr::get_default_resource())
    : os_(os), next_(next) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
      auto* addr = next_->allocate(bytes, align);
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    next_->deallocate(addr, bytes, align);
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    auto p = dynamic_cast(&other);
    return p && next_ == p->next_;
    }
    std::ostream& os_;
    std::pmr::memory_resource* next_;
    };
    Use it to
    allocate and
    deallocate
    memory

    View Slide

  73. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 73/112
    Improving the tracing resource
    class tracing_resource final : public std::pmr::memory_resource
    {
    public:
    tracing_resource(std::ostream& os,
    std::pmr::memory_resource* next = std::pmr::get_default_resource())
    : os_(os), next_(next) {}
    private:
    void* do_allocate(size_t bytes, size_t align) override    {
      auto* addr = next_->allocate(bytes, align);
    os_ << "allocate(" << bytes << ", " << align << ") -> " << addr << '\n';
    return addr;
    }
    void do_deallocate(void* addr, size_t bytes, size_t align) override {
    os_ << "deallocate(" << addr << ", " << bytes << ", " << align << ")\n";
    next_->deallocate(addr, bytes, align);
    }
    bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
    auto p = dynamic_cast(&other);
    return p && next_ == p->next_;
    }
    std::ostream& os_;
    std::pmr::memory_resource* next_;
    };
    Use the default
    memory resource
    if none is given

    View Slide

  74. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 74/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };

    View Slide

  75. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 75/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    Back to chosing
    an allocator for the
    word frequency
    histogram

    View Slide

  76. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 76/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    std::pmr::unsynchronized_pool_resource r;
    histogram hist(&r);
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }

    View Slide

  77. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 77/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::string word;
    std::pmr::unsynchronized_pool_resource r;
    histogram hist(&r);
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }
    I choose an
    unsynchronized
    pool resource.

    View Slide

  78. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 78/112
    Live Demo

    View Slide

  79. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 79/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };

    View Slide

  80. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 80/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::unsynchronized_pool_resource r;
    std::pmr::string word(&r);
    histogram hist(&r);
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }

    View Slide

  81. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 81/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::unsynchronized_pool_resource r;
    std::pmr::string word(&r);
    histogram hist(&r);
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }
    84 allocations
    down from
    133000

    View Slide

  82. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 82/112
    PMR histogram
    class histogram {
    public:
    histogram(const std::pmr::polymorphic_allocator<>& alloc = {}): words_(alloc){}
    void add(const std::pmr::string& word) {
    ++words_[word];
    }
    void print_top(size_t n, std::ostream& os) const {
    using count = std::pair;
    std::vector popular(words_.begin(), words_.end());
    std::ranges::partial_sort(popular,
    popular.begin() + n,
    std::greater{},
    &count::second);
    for (const auto& stat : popular | std::ranges::views::take(n)) {
    os << stat.first << '\t' << stat.second << '\n';
    }
    }
    private:
    std::pmr::unordered_map words_;
    };
    int main()
    {
    std::pmr::unsynchronized_pool_resource r;
    std::pmr::string word(&r);
    histogram hist(&r);
    while (std::cin >> word) {
    hist.add(word);
    }
    hist.print_top(5, std::cout);
    }
    But you really need tools
    like heaptrack to find the
    places you’ve missed.

    View Slide

  83. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 83/112
    PMR for your types
    class counted_string
    {
    public:
    counted_string(size_t count, std::pmr::string str)
    : str_(std::move(str))
    , count_(count)
      {}
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh)
    {
    return lh.count_ < rh.count_;
    }
    protected:
    std::pmr::string str_;
    size_t count_;
    };

    View Slide

  84. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 84/112
    https://godbolt.org/z/8c4eKcxhh

    View Slide

  85. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 85/112
    PMR for your types
    A PMR enabled type needs:

    View Slide

  86. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 86/112
    PMR for your types
    A PMR enabled type needs:
    – A public allocator_type type

    View Slide

  87. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 87/112
    PMR for your types
    A PMR enabled type needs:
    – A public allocator_type type
    – Constructors with the allocator type as the last
    argument

    View Slide

  88. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 88/112
    PMR for your types
    A PMR enabled type needs:
    – A public allocator_type type
    – Constructors with the allocator type as the last
    argument
    – Variadic constructors with
    std::allocator_arg_t and allocator_type
    as the first parameters

    View Slide

  89. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 89/112
    PMR for your types
    A PMR enabled type needs:
    – A public allocator_type type
    – Constructors with the allocator type as the last
    argument
    – Variadic constructors with
    std::allocator_arg_t and allocator_type
    as the first parameters
    – And a copy-like constructor with allocator

    View Slide

  90. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 90/112
    PMR for your types
    A PMR enabled type needs:
    – A public allocator_type type
    – Constructors with the allocator type as the last
    argument
    – Variadic constructors with std::allocator_arg_t
    and allocator_type as the first parameters
    – And a copy-like constructor with allocator
    – And move-like constructor with allocator

    View Slide

  91. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 91/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };

    View Slide

  92. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 92/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };
    Signal that this type
    uses a PMR allocator.

    View Slide

  93. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 93/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };
    Constructor with
    allocator last

    View Slide

  94. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 94/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };
    Variadic constructor with
    non-allocator args at end

    View Slide

  95. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 95/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };
    Copy-like constructor
    with allocator, used
    by containers

    View Slide

  96. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 96/112
    PMR for your types
    class counted_string
    {
    public:
    using allocator_type = std::pmr::polymorphic_allocator<>;
    counted_string(size_t count,std::pmr::string str, const allocator_type& alloc = {});
    template
    counted_string(std::allocator_arg_t,  const allocator_type& alloc,
    size_t count, Ts&& ... ts);
    counted_string(const counted_string& orig);
    counted_string(const counted_string& orig, const allocator_type& alloc);
    counted_string(counted_string&& orig) noexcept;
    counted_string(counted_string&& orig, const allocator_type& alloc) noexcept;
    std::string_view str() const { return str_; }
    size_t count() const { return count_; }
    friend bool operator<(const counted_string& lh, const counted_string& rh);
    protected:
    std::pmr::string str_;
    size_t count_;
    };
    Move-like constructor
    with allocator, used
    by containers

    View Slide

  97. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 97/112
    https://godbolt.org/z/aoYPWGxP8

    View Slide

  98. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 98/112
    In conclusion

    View Slide

  99. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 99/112
    In conclusion

    Making your own types use PMR allocators is
    not hard, but it’s easy to make mistakes

    View Slide

  100. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 100/112
    In conclusion

    Making your own types use PMR allocators is
    not hard, but it’s easy to make mistakes

    Using PMR types with standard containers is
    easy

    View Slide

  101. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 101/112
    In conclusion

    Making your own types use PMR allocators is
    not hard, but it’s easy to make mistakes

    Using PMR types with standard containers is
    easy

    PMR is very flexible because the memory
    resources and the types that uses them are
    decoupled

    View Slide

  102. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 102/112
    In conclusion

    Making your own types use PMR allocators is not
    hard, but it’s easy to make mistakes

    Using PMR types with standard containers is easy

    PMR is very flexible because the memory resources
    and the types that uses them are decoupled

    Memory usage is quite opaque, so it’s not easy to
    see where you’ve made mistakes

    View Slide

  103. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 103/112
    In conclusion

    Making your own types use PMR allocators is not hard, but it’s
    easy to make mistakes

    Using PMR types with standard containers is easy

    PMR is very flexible because the memory resources and the
    types that uses them are decoupled

    Memory usage is quite opaque, so it’s not easy to see where
    you’ve made mistakes

    PMR is still not a panacea for your memory management
    needs, but it’s often sufficient

    View Slide

  104. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 104/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg

    View Slide

  105. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 105/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.

    View Slide

  106. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 106/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.
    Not even when move-assigning.

    View Slide

  107. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 107/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.
    Not even when move-assigning.

    If resource operator== says the
    memory resources are the same,
    then the internal pointers are taken over,
    otherwise it’s element-wise move
    assignment to newly allocated memory.

    View Slide

  108. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 108/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.
    Not even when move-assigning.

    If resource operator== says the
    memory resources are the same,
    then the internal pointers are taken over,
    otherwise it’s element-wise move
    assignment to newly allocated memory.

    View Slide

  109. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 109/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.
    Not even when move-assigning.

    If resource operator== says the
    memory resources are the same,
    then the internal pointers are taken over,
    otherwise it’s element-wise move
    assignment to newly allocated memory.

    View Slide

  110. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 110/112
    Just one more thing...
    http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
    A pmr container never changes its memory
    resource.
    Not even when move-assigning.

    If resource operator== says the
    memory resources are the same,
    then the internal pointers are taken over,
    otherwise it’s element-wise move
    assignment to newly allocated memory.

    View Slide

  111. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 111/112
    Learn more
    https://tinyurl.com/cppweekly-on-pmr
    YouTube playlist with 6
    episodes of Jason Turner’s
    C++Weekly covering PMR
    https://youtu.be/RLezJuqNcEQ
    CppConn 2019 – Pablo Halpern and
    Alisdair Meredith, “Congratulations!
    You survived C++11 allocators!”

    View Slide

  112. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 112/112
    [email protected]
    @[email protected]
    @rollbear
    Björn Fahller
    Most Malleable Memory Management Method

    View Slide