Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

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] 2/112 Most

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

    Malleable Memory Management Method Björn Fahller
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 14/112 So

    you turn to allocators... https://www.edvardmunch.org/the-scream.jsp
  9. 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
  10. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 16/112 Polymorphic

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

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

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

    Memory Resource “container” PMR allocator <<memory resource>> 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<int>
  14. 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; }; <memory_resource>
  15. 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; }; <memory_resource> Inherit and implement these
  16. 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; }; <memory_resource> Mildly annoying that alignment uses size_t and not align_val_t Inherit and implement these
  17. 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_; };
  18. 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_; };
  19. 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_; };
  20. 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_; };
  21. 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_; };
  22. 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.
  23. 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
  24. 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
  25. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  26. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  27. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  28. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  29. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  30. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  31. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; };
  32. 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::string, size_t>;         std::vector<count> 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<std::string, size_t> words_; }; int main() { std::string word; histogram hist; while (std::cin >> word) { hist.add(word); } hist.print_top(5, std::cout); }
  33. 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!
  34. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; };
  35. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; PMR-ify the container and strings
  36. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; Get an allocator to use
  37. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; In C++17 it was necessary to express a type here, but in C++20 onwards, just use the diamond
  38. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; int main() { std::pmr::string word; histogram hist; while (std::cin >> word) { hist.add(word); } hist.print_top(5, std::cout); }
  39. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; int main() { std::pmr::string word; histogram hist; while (std::cin >> word) { hist.add(word); } hist.print_top(5, std::cout); }
  40. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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.
  41. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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!
  42. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 51/112 Choosing

    a memory resource The header <memory_resource> 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
  43. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 52/112 Choosing

    a memory resource The header <memory_resource> 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.
  44. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 53/112 Choosing

    a memory resource The header <memory_resource> 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.
  45. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 54/112 Choosing

    a memory resource The header <memory_resource> 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.
  46. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 55/112 Choosing

    a memory resource The header <memory_resource> 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.
  47. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 56/112 Choosing

    a memory resource The header <memory_resource> 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.
  48. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 57/112 Choosing

    a memory resource The header <memory_resource> 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
  49. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 58/112 Choosing

    a memory resource The header <memory_resource> 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);
  50. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 59/112 Choosing

    a memory resource The header <memory_resource> 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.
  51. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 60/112 Choosing

    a memory resource The header <memory_resource> 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.
  52. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 61/112 Choosing

    a memory resource The header <memory_resource> 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.
  53. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 62/112 Choosing

    a memory resource The header <memory_resource> 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.
  54. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 63/112 Choosing

    a memory resource The header <memory_resource> 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);
  55. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 64/112 Choosing

    a memory resource The header <memory_resource> 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.
  56. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 65/112 Choosing

    a memory resource The header <memory_resource> 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.
  57. Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 66/112 Choosing

    a memory resource The header <memory_resource> 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
  58. 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_; };
  59. 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?
  60. 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<const tracing_resource*>(&other); return p && next_ == p->next_; } std::ostream& os_; std::pmr::memory_resource* next_; };
  61. 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<const tracing_resource*>(&other); return p && next_ == p->next_; } std::ostream& os_; std::pmr::memory_resource* next_; };
  62. 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<const tracing_resource*>(&other); return p && next_ == p->next_; } std::ostream& os_; std::pmr::memory_resource* next_; }; Get an underlying memory resource
  63. 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<const tracing_resource*>(&other); return p && next_ == p->next_; } std::ostream& os_; std::pmr::memory_resource* next_; }; Use it to allocate and deallocate memory
  64. 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<const tracing_resource*>(&other); return p && next_ == p->next_; } std::ostream& os_; std::pmr::memory_resource* next_; }; Use the default memory resource if none is given
  65. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; };
  66. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; }; Back to chosing an allocator for the word frequency histogram
  67. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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); }
  68. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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.
  69. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> words_; };
  70. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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); }
  71. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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
  72. 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::pmr::string, size_t>; std::vector<count> 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<std::pmr::string, size_t> 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.
  73. 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_; };
  74. 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
  75. 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
  76. 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
  77. 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
  78. 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
  79. 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 <typename ... Ts> 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_; };
  80. 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 <typename ... Ts> 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.
  81. 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 <typename ... Ts> 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
  82. 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 <typename ... Ts> 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
  83. 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 <typename ... Ts> 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
  84. 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 <typename ... Ts> 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
  85. 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
  86. 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
  87. 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
  88. 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
  89. 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
  90. 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
  91. 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.
  92. 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.
  93. 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.
  94. 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.
  95. 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.
  96. 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.
  97. 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!”