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

TechTown - Most Malleable Memory Management Method

Björn Fahller
September 20, 2023
49

TechTown - 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

September 20, 2023
Tweet

Transcript

  1. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 1/110 Most

    Malleable Memory Management Method Björn Fahller
  2. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 6/110 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
  3. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 7/110 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
  4. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 8/110 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
  5. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 9/110 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
  6. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 10/110 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
  7. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 12/110 So

    you turn to allocators... https://www.edvardmunch.org/the-scream.jsp
  8. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 13/110 So

    you turn to allocators... https://www.edvardmunch.org/the-scream.jsp But PMR allocators suck less
  9. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 14/110 Polymorphic

    Memory Resource “container” PMR allocator <<memory resource>>
  10. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 15/110 Polymorphic

    Memory Resource “container” PMR allocator <<memory resource>> A “container” has a PMR allocator
  11. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 16/110 Polymorphic

    Memory Resource “container” PMR allocator <<memory resource>> A “container” has a PMR allocator A PMR allocator references a memory resource
  12. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 17/110 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>
  13. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 18/110 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>
  14. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 19/110 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
  15. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 20/110 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
  16. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 21/110 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_; };
  17. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 22/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 23/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 24/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 25/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 27/110 Most

    Malleable Memory Management Method It is very important that your memory resource instance outlives all containers that use it.
  22. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 28/110 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
  23. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 29/110 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
  24. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 30/110 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_; };
  25. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 31/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 32/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 33/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 34/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 35/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 36/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 37/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 38/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 40/110 Most

    Malleable Memory Management Method Nothing surprising here, but good to have a baseline and a tool Let’s PMR it!
  34. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 41/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 42/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 43/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 44/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 45/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 47/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 48/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 49/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 50/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 51/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 52/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 53/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 54/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 55/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 56/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 57/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 58/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 59/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 60/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 61/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 62/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 63/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 64/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 65/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 66/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 67/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 68/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 69/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 70/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 71/110 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
  64. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 72/110 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
  65. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 73/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 74/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 75/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 76/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 78/110 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::pmr::vector<count> popular(words_.begin(), words_.end(), words_.get_allocator()); 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 79/110 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::pmr::vector<count> popular(words_.begin(), words_.end(), words_.get_allocator()); 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_; }; 84 allocations down from 133000
  71. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 80/110 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::pmr::vector<count> popular(words_.begin(), words_.end(), words_.get_allocator()); 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_; }; But you really need tools like heaptrack to find the places you’ve missed.
  72. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 81/110 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_; };
  73. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 84/110 PMR

    for your types A PMR enabled type needs: – A public allocator_type type
  74. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 85/110 PMR

    for your types A PMR enabled type needs: – A public allocator_type type – Constructors with the allocator type as the last argument
  75. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 86/110 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
  76. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 87/110 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
  77. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 88/110 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
  78. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 89/110 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_; };
  79. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 90/110 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.
  80. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 91/110 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
  81. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 92/110 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
  82. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 93/110 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
  83. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 94/110 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
  84. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 97/110 In

    conclusion • Making your own types use PMR allocators is not hard, but it’s easy to make mistakes
  85. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 98/110 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
  86. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 99/110 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
  87. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 100/110 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
  88. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 101/110 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
  89. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 102/110 Just

    one more thing... http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg
  90. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 103/110 Just

    one more thing... http://mojtv.hr/images/fb2de057-6db9-45f8-ba8a-6891149a4a87.jpg A pmr container never changes its memory resource.
  91. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 104/110 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.
  92. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 105/110 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.
  93. Mmmmm – NDC{TechTown} 2023 © Björn Fahller @[email protected] 106/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 107/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 108/110 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 – NDC{TechTown} 2023 © Björn Fahller @[email protected] 109/110 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!”