Share
Embed
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
Transcript
112 slides
-
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 2/112 Most
Malleable Memory Management Method Björn Fahller -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 3/112 Most
Malleable Memory Management Method -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 4/112 Most
Malleable Memory Management Method -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 5/112 Most
Malleable Memory Management Method -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 6/112 Most
Malleable Memory Management Method -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 7/112 Most
Malleable Memory Management Method Björn Fahller -
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 -
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 -
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 -
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 -
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 -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 13/112 So
you turn to allocators... -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 14/112 So
you turn to allocators... https://www.edvardmunch.org/the-scream.jsp -
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 -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 16/112 Polymorphic
Memory Resource “container” PMR allocator <<memory resource>> -
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 -
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 -
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> -
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> -
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 -
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 -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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. -
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 -
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 -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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_; }; -
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); } -
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! -
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_; }; -
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 -
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 -
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 -
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); } -
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); } -
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. -
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! -
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 -
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. -
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. -
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. -
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. -
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. -
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 -
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); -
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. -
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. -
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. -
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. -
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); -
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. -
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. -
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 -
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_; }; -
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? -
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_; }; -
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_; }; -
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 -
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 -
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 -
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_; }; -
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 -
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); } -
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. -
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_; }; -
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); } -
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 -
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. -
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_; }; -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 85/112 PMR
for your types A PMR enabled type needs: -
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 -
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 -
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 -
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 -
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 -
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_; }; -
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. -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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. -
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. -
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. -
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. -
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. -
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. -
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!” -
Mmmmm – C++OnSea 2023 © Björn Fahller @[email protected] 112/112 [email protected]
@[email protected] @rollbear Björn Fahller Most Malleable Memory Management Method