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.

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

Popular posts from this blog

Morning Quotes

QCalendarWidget CSS Stylesheeting

Selecting new phone plan