Boost Pool experience requested. Is it useful as allocator with preallocation?
Asked Answered
M

1

7

Recently i have been looking for a pool/allocator mechanism. Boost Pool seems to provide the solution, but there is still things, which it have not been able to deduce from the documentation.

What need to be allocated

  1. Several small classes (~30 chars)
  2. std::map (i want to ensure it do not perform dynamic allocator by itself)
  3. allocation within pugi::xml
  4. std::strings

How to control of address space for allocation (or just amount)

The object_pool seem the to provide a good way for allocating need 1) However it would like to set a fixed size for the allocator to use. By default it grabs memory be ifself. If possible i would like to give it the address space it can play within.

char * mem_for_class[1024*1024];
boost::object_pool<my_class,mem_for_class> q;

or:

const int max_no_objs=1024;
boost::object_pool<my_class,max_no_objs> q;

Although the UserAllocator is available in Boost::Pool; it seem to defeat the point. I am afraid the control needed would make it too inefficient... and it would be better to start from scratch.

It it possible to set a fixed area for pool_allocator ?

The question is a bit similar to the first. Do boost pool provide any way of limiting how much / where there is allocated memory when giving boost::pool_allocator to a std-type-class (e.g. map)

My scenario

Embedded linux programming. The system must keep running for..ever. So we can not risk any memory segmentation. Currently i mostly either static allocation (stack), but also a few raw "new"s. I would like an allocation scheme that ensure i use the same memory area each time the program loops. Speed /space is important, but safety is still top priority.

I hope StackOverflow is the place to ask. I tried contacting the author of Boost::Pool "Stephen" without luck. I have not found any Boost-specific forum.

Marcy answered 16/2, 2014 at 14:2 Comment(0)
W
6

You can always create an allocator that works with STL. If it works with STL, it should work with boost as you are able to pass boost allocators to STL containers..

Considering the above, an allocator that can allocate at a specified memory address AND has a size limitation specified by you can be written as follows:

#include <iostream>
#include <vector>

template<typename T>
class CAllocator
{
    private:
        std::size_t size;
        T* data = nullptr;

    public:
        typedef T* pointer;
        typedef const T* const_pointer;

        typedef T& reference;
        typedef const T& const_reference;

        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;

        typedef T value_type;


        CAllocator() {}
        CAllocator(pointer data_ptr, size_type max_size) noexcept : size(max_size), data(data_ptr) {};

        template<typename U>
        CAllocator(const CAllocator<U>& other) noexcept {};

        CAllocator(const CAllocator &other) : size(other.size), data(other.data) {}

        template<typename U>
        struct rebind {typedef CAllocator<U> other;};

        pointer allocate(size_type n, const void* hint = 0) {return &data[0];}
        void deallocate(void* ptr, size_type n) {}
        size_type max_size() const {return size;}
};

template <typename T, typename U>
inline bool operator == (const CAllocator<T>&, const CAllocator<U>&) {return true;}

template <typename T, typename U>
inline bool operator != (const CAllocator<T>& a, const CAllocator<U>& b) {return !(a == b);}





int main()
{
    const int size = 1024 / 4;
    int ptr[size];
    std::vector<int, CAllocator<int>> vec(CAllocator<int>(&ptr[0], size));

    int ptr2[size];
    std::vector<int, CAllocator<int>> vec2(CAllocator<int>(&ptr2[0], size));

    vec.push_back(10);
    vec.push_back(20);
    vec2.push_back(30);
    vec2.push_back(40);


    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }

    std::cout<<"\n\n";

    vec2 = vec;

    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }

    std::cout<<"\n\n";
    vec2.clear();

    vec2.push_back(100);
    vec2.push_back(200);

    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }
}

This allocator makes sure that all memory is allocated at a specified address. No more than the amount you specify can be allocated with the freedom to allocate were you want whether it is on the stack or the heap.

You may create your own pool or use a std::unique_ptr as the pool for a single container.

EDIT: For strings, you need an offset of sizeof(_Rep_base). See: Why std::string allocating twice?

and http://ideone.com/QWtxWg

It is defined as:

struct _Rep_base
{
    std::size_t     _M_length;
    std::size_t     _M_capacity;
    _Atomic_word        _M_refcount;
};

So the example becomes:

struct Repbase
{
    std::size_t     length;
    std::size_t     capacity;
    std::int16_t    refcount;
};

int main()
{
    typedef std::basic_string<char, std::char_traits<char>, CAllocator<char>> CAString;

    const int size = 1024;
    char ptr[size] = {0};

    CAString str(CAllocator<char>(&ptr[0], size));
    str = "Hello";

    std::cout<<&ptr[sizeof(Repbase)];
}
Wallis answered 4/4, 2014 at 19:43 Comment(8)
Strange, allocate ... {return &data[0];} - wouldn't that always return the same address?Pulcheria
Yes. It would always return the same address. However, as far as I can tell, all the STL containers keep track of their own memory and only call "allocate" when they need more memory. Otherwise they call "contruct" to construct the elements. Try it out and see.Wallis
Although not boost::pool related, THANKS! Fine example of the std::vector. I have only tried briefly, but i can see how memory content change using debugger. BTW: Sorry for the late response, the answer hit in the beginning of a vacatoin. However i still need a little help (or selfstudy) to figure out how to use std::string (or rather configure basic_string to use this allocator).Marcy
ideone.com/QWtxWg See here for why you need that offset for strings: #21979830Wallis
Thanks again for quick adjustment! After your first post i tried something similar but had issues with the syntax/parameters of the template. The use of the offset seem clear enough based on the declared RepBase struct. The string can still output using the str-class itself std::cout<<str; Is there a particular reason you index the memory for allocation? CAString str(CAllocator<char>(ptr, size)); // seem to work for me The string class ( or its allocator) fails to detect out-of-space and result in segmentation fault. I guess this is however not worse than old cstrings.Marcy
One still thing wonders me. You allocator do not seem to inherit/implement and allocator interface. Although it has the methods, it is not declared... or have i missed something?Marcy
I will tryout a few more the std::classes as map the next daysMarcy
You do not need all of allocator interface because allocator_traits automatically add whatever is missing.Wallis

© 2022 - 2024 — McMap. All rights reserved.