How to reuse memory previously allocated

Hello All,

I have a question about memory allocation.

I have a function that calls a lot of object constructors, which in return these constructors will allocate a lot of memory.

Now, in my program I am sure that if I first call this function , say it will call the constructor of 100 object.

If I call this function again and again, I am sure that it will only call the constructor 100 times again, and thus I am sure that the memory allocated in the first call can be reused again.

How can I reuse the memory allocated in the first call?
Can I use something like boost:object_pool so that I can tell the pool to restart from the begining and do not allocate extra memory, just use what you already have?

Thank you in advance


You have to create a class that manages these "allocated memory pool" so that whenever you want to allocate new memory, you tell this class to allocate you a memory.

So, if a memory has already been allocated but isn't being used, then this class will return the address to that memory.

Of course, this class will have to keep track of the allocated memory that isn't used.
1
2
3
4
5
class memory
{
    ...
}
int *ptr=memory.getMemory(size)

Either that, or you just delete all the memory that you allocated for your class in your deconstructor

1
2
3
4
5
6
7
8
9
10
11
12
13
class something
{
    int *pointer;
public:
    something(); //This is a constructor
    {
        int *pointer=new int[10];
    }
    ~something(); //This is a deconstructor
    {
        delete [] pointer;
    }
};
Last edited on
Thank you threeright,

I just do not want to keep calling new/delete every time. Because I am sure that if I call the main function again and again it will need the exact same size of the memory every time.

Is there any thing in the std:: or the boost:: libraries that does your first suggested option?

Thank you

> Can I use something like boost:object_pool

boost:object_pool<> does not throw std::bad_alloc on failure; it is not a singleton pool, so we have to manage the pool lifetime explicitly; and it is not a standard library compatible allocator.

boost::pool_allocator<> and boost::fast_pool_allocator<> are singleton pools with exceptions and are written as standard library compatible allocators. Though you would find that they are a wee bit more expensive, their usage is far more convenient.

For instance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <vector>
#include <map>
#include <boost/pool/pool_alloc.hpp>
#include <memory>
#include <ctime>
#include <iostream>

struct A final
{
    static inline void* operator new( std::size_t )
    { return pool.allocate() ; }

    static inline void operator delete( void* p )
    { pool.deallocate( static_cast<A*>(p) ) ; }

    A()
    {
        vector.reserve(1000) ;
        for( int i=0 ; i<100 ; ++i ) map.insert( std::make_pair( i, this ) ) ;
    }

    std::vector< A*, boost::pool_allocator<A*> > vector ;
    std::map< int, A*, std::less<int>,
              boost::fast_pool_allocator< std::pair<const int, A* > > > map ;
    char other_data[50] ;
    static boost::fast_pool_allocator<A> pool ;
};

boost::fast_pool_allocator<A> A::pool ;

void foo()
{
    constexpr int N = 10000 ;
    std::unique_ptr<A> objects[N] ;
    // create 10K objects
    for( int i = 0 ; i<N ; ++i ) objects[i] = std::unique_ptr<A>( new A ) ;
    // destructor of std::unique_ptr<A> will destroy the objects of type A
    // and return memory to A::pool
}

int main()
{
    std::clock_t start = std::clock() ;
    for( int i = 0 ; i < 10 ; ++i ) foo() ;
    std::cout << ( std::clock() - start ) / double(CLOCKS_PER_SEC) << " secs.\n" ;
    // total: 100K object allocs + 100K vector resizes + 10M map inserts
    // on my laptop, this took 1.3 seconds
}
Thank you JLBorges
I just have couple of questions:

1) Why do we need the A::vector and A::map? are they required in the memory allocation algorithm or just some data.

2) You said that there is a total of 100k objects allocated. Which means that every time foo() is called another 10000 object are allocated.
What I want is at the first time the foo() is allocated, only 10000 is allocated. Then the next time foo() is called we reuse the memory. It is like moving the pointer of the pool back to the beginning and re using the memory.


Thank you
> 1) Why do we need the A::vector and A::map?
> are they required in the memory allocation algorithm or just some data.

We don't need them to use a pool. Just some data to illustrate how boost pool allocators can be used with standard library containers.


> What I want is at the first time the foo() is allocated, only 10000 is allocated.
> Then the next time foo() is called we reuse the memory.

That is essentially what the pool does. It allocates memory in big chunks; sub-allocates out of the chunks; and returns the memory to the chunk when a block is deallocated.
See: http://www.boost.org/doc/libs/1_55_0/libs/pool/doc/html/boost_pool/pool/pooling.html

We can check it out by writing a small test program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <vector>
#include <boost/pool/pool_alloc.hpp>
#include <memory>
#include <ctime>
#include <iostream>
#include <cstring>
#include <new>

struct A final
{
    static inline void* operator new( std::size_t )
    { return pool.allocate() ; }

    static inline void operator delete( void* p )
    { pool.deallocate( static_cast<A*>(p) ) ; }

    void tag( const char* cstr ) { std::strcpy(data,cstr) ; }
    const char* tag() const { return data ; }

    char filler[16] ;
    char data[50] ;

    static boost::fast_pool_allocator<A> pool ;
};

boost::fast_pool_allocator<A> A::pool ;

void foo( const char* current_tag ) // uses boost pool library
{
    std::unique_ptr<A> pa1( new A ) ;
    std::unique_ptr<A> pa2( new A ) ;

    A* pa3 = new A ;
    A* pa4 = new A ;

    std::cout << "objects are at: " << pa1.get() << ' ' << pa2.get() << ' '
               << pa3 << ' ' << pa4 << '\n' ;
    
    // verify that the same memory is being reused: check if the object still contains 
    // the char data that we stored into it the last time the function was called
    std::cout << "current tag: \"" << current_tag << '"' ;
    static bool first_time = true ;
    if( !first_time) std::cout << "  old tag: \"" << pa4->tag() << '"' ;
    std::cout << '\n' ;
    first_time = false ;

    std::vector< A, boost::pool_allocator<A> > seq(1000) ;
    std::cout << "objects in vector are at: " << &seq.front()
               << " to " << &seq.back() << "\n\n" ;

    pa1->tag(current_tag) ;
    pa2->tag(current_tag) ;
    pa3->tag(current_tag) ;
    pa4->tag(current_tag) ;

    delete pa4 ;
    delete pa3 ;
}

void bar() // home-grown pool with placement new (trivialized)
{
    static constexpr std::size_t MAX_OBJECTS = 1000 ;
    static char buffer[ MAX_OBJECTS * sizeof(A) ] ;

    // construct an object with placement new
    int cnt = 0 ;
    const auto new_A = [&cnt]
    {
        if( cnt == MAX_OBJECTS ) throw std::bad_alloc() ;
        return ::new (buffer + cnt++ * sizeof(A) ) A ;
    };

    // create objects
    A* array[MAX_OBJECTS] ;
    for( std::size_t i = 0 ; i<MAX_OBJECTS ; ++i ) array[i] = new_A() ;

    // use them ...

    // destroy the objects 
    for( A* ptr : array ) ptr->~A() ;
}

int main()
{
    for( const char* tag : { "one", "two", "three", "four" } )
    {
        foo(tag) ; bar() ;
        bar() ;
    }
}

http://coliru.stacked-crooked.com/a/c1d7c9e292b1882f
Thank you JLBorges,
This is really what I want.

Topic archived. No new replies allowed.