Playing with C++ "Polymorphic Memory Resources"
Recently I was looking into heap-less programming topic using C++. Turns out the std::pmr namespace provides a bunch of useful stuff to create memory pools wherever you want - globally, on stack etc, and then you can avoid a bunch of new-delete calls.
Below is some code samples I found in a paper at https://www.rkaiser.de/wp-content/uploads/2021/03/embo2021-pmr-STL-for-Embedded-Applications-en.pdf
I added some notes at the end about what I observed in my usage - especially the problem of bad-allocations which was different than expected behavior. But it could be the compiler too. Anyway I did found a workaround to safely handle that too.
Cheers!!
// ---------------------------------------------
#include <iostream>
#include <vector>#include <iomanip>
#include <memory_resource>
#include <array>
int new_counter = 0;
int delete_counter = 0;
size_t allocated_mem = 0;
void showmemory(unsigned char* buffer, std::size_t buffer_size, const char* headline);
void reset_counter()
{
new_counter = 0;
delete_counter = 0;
allocated_mem = 0;
}
void new_delete_summary()
{
std::cout << std::dec << "#new: " << new_counter << " #delete: "
<< delete_counter << " #bytes: " << allocated_mem << std::endl;
reset_counter();
}
void* operator new(std::size_t sz)
{ // Like the predefined new operator, this operator should only
// call malloc and throw an exception if necessary
void* ptr = std::malloc(sz);
if (ptr)
{
new_counter++; // The only differences from the predefined
allocated_mem += sz;// new-Operator
return ptr;
}
else throw std::bad_alloc{};
}
void operator delete(void* ptr) noexcept
{
delete_counter++;
std::free(ptr);
}
void one_explicit_new_and_delete()
{
int* pi = new int;
showmemory((unsigned char*)pi, sizeof(int), "Printing integer at pi" );
delete pi;
}
void vector_with_implicit_heap_allocations()
{
std::vector<int> v;
for (int i = 0; i < 10; i++)
v.push_back(i);
showmemory((unsigned char*)&v, sizeof(v), "Printing vector");
}
void showmemory(unsigned char* buffer, std::size_t buffer_size, const char* headline = "")
{
if (headline != "")
std::cout << headline << std::endl;
std::cout << "&buffer=0x" << std::hex << (unsigned long)(buffer)
<< " " << std::dec << buffer_size << " bytes" << std::endl;
int i = 0;
while (i < buffer_size)
{
int first = i;
int last = i + std::min(10, int(buffer_size - first));
std::cout << "&=" << std::setw(2) << std::hex <<
std::size_t(first);
std::cout << " asc: ";
for (int k = first; k < last; k++)
{
if ((buffer[k] >= 32) and (buffer[k] <= 127))
std::cout << buffer[k];
else
std::cout << ".";
}
i = i + 10;
std::cout << std::endl;
}
std::cout << std::endl;
}
void vector_with_heap_memory(int n)
{
std::vector<std::string> container;
// work with the container
for (int i = 0; i < n; ++i)
{ // No small string optimization (SSO) is desired here:
container.push_back("A string with more than 16 chars");
}
}
void vector_with_stack_memory(int n)
{ // The only difference are the first three statements:
std::array<unsigned char, 100'000> memory; // local definition
// use memory as memory for the vector and the strings:
std::pmr::monotonic_buffer_resource pool{ memory.data(), memory.size() };
std::pmr::vector<std::pmr::string> container{&pool}; // see 7.
// work with the container
for (int i = 0; i < n; ++i)
{
container.push_back("A string with more than 16 chars");
}
}
void use_strings_without_heap_allocations()
{
std::array<unsigned char, 100'000> memory;
std::pmr::monotonic_buffer_resource string_pool{ memory.data(),
memory.size() };
std::pmr::string s1("This is a string", &string_pool);
std::pmr::string s2("This is another string", &string_pool);
s1 += s2;
int p=s1.find("stringThis");
}
std::array<unsigned char, 100'000> memory; // global definition
void vector_with_global_memory(int n)
{ // The definition of memory shifted out of the function.
// everything else the same as vector_with_stack_memory
std::pmr::monotonic_buffer_resource pool{ memory.data(),
memory.size() };
std::pmr::vector<std::pmr::string> container{ &pool };
// work with the container
for (int i = 0; i < n; ++i)
{
container.push_back("A string with more than 16 chars");
}
}
void vector_with_stack_memory_with_exception_on_bad_alloc()
{ // The only difference are the first three statements:
std::array<unsigned char, 1000> memory; // local definition
// use memory as memory for the vector and the strings:
std::pmr::monotonic_buffer_resource pool{ memory.data(), memory.size(),
std::pmr::null_memory_resource() }; // Added null_memory_resource() to give exception when run out of memory
std::pmr::vector<std::pmr::string> container{&pool}; // see 7.
// work with the container
for (int i = 0; i < 100; ++i) // needs 3200+ bytes of memory
{
container.push_back("A string with more than 16 chars");
}
}
int main() {
// basic overloaded new-delete operators
// one_explicit_new_and_delete();
// new_delete_summary();
// reset_counter();
// vector_with_implicit_heap_allocations();
// new_delete_summary();
// Usage of pmr
// vector_with_heap_memory(3); // a vector on heap memory
// new_delete_summary();
// reset_counter();
// vector_with_stack_memory(3); // vector using pmr but stack memory
// new_delete_summary();
// reset_counter();
// use_strings_without_heap_allocations();
// new_delete_summary();
//reset_counter();
//vector_with_global_memory(3400);
//new_delete_summary();
// how to handle running out of memory
try
{
vector_with_stack_memory_with_exception_on_bad_alloc();
}
catch(std::exception& err)
{
std::cout << "Error with allocation: " << err.what() << "\n";
}
return 0;
}
// Notes: Usually have some std::array as buffer to be used by monotonic_buffer_resource object;
// Monotonic buffer object is passed to all std::pmr types
// In case required memory turns out to be less than buffer-size, heap allocations are supposed to happen,
// But from what I have seen, it does not happen like that.
// Generally a stack overflow happens - this could also be compiler dependent behavior.
// So usually adding null_memory_resource() as 3rd argument works to avoid this;
// Although with this option, an exception is raised;
Comments
Post a Comment