Is it more efficent to allocate a small object on the stack as apposed to creating it on the heap (once)?

StackOverflow https://stackoverflow.com/questions/14707670

Question

Currently with the library I have written, my small objects (which are not polymorphic) are allocated, within an object-pool, in a vector with unique_ptr's. Now obviously I want to change this because there's obviously a lot of over-head in calling new so many times. I'm curious if it is more efficient to: cache the objects in the pool (store it in a vector, i.e. vector<Object>), or create the object when it is needed, via it's ID. And note that there is a lot of objects that are created.

What I mean is, should I be doing this:

Create the object when needed? (Note these objects are small, 64-128 bits, as all is contained is an ID and a reference/pointer to a parent object)

Object ObjectFactory::create()
{
    return Object(nextId(), getParent());
}

Object ObjectFactory::get(unsigned id)
{
    return Object(id, getParent());
}

or:

Object& ObjectFactory::create()
{
     // get the next id of the object
     unsigned id = nextId();

     // resize (if necessary)
     if(_objects.size() <= id)
     {
        _objects.resize(id + 1);
        _objects[id]._parent = getParent();
     }

     return _objects[id];
}

Object& ObjectFactory::get(unsigned id)
{ return _objects[id]; }

What I'm specifically concerned about is: would the re-creation of the Object's cause much over-head?

Was it helpful?

Solution

@LokiAstari is right, you clearly have a problem with your pointers on resize.

There's something I don't understand; you say you're using an object pool but that you have problems with too many new statements. If you're using an object pool, I would say it's precisely to avoid new statements, no?

Here are my suggestions, although I'm no expert and there might be better solutions involving the implementation of your own allocator typically (dark side of the force..). You could use a container like std::deque, which ensures the validity of pointers/references on resize.

You would begin with an initial resize of a lot of Objects (your pool), and you can either handle manually the subsequent resize when needed (extending the capacity with chunks of predefined size), or accept the new statements then if you know there shouldn't be a lot, and use the emplace_back method.

I also don't know if you're doing a lot of insertion/deletion of objects with your IDs. If so, you might consider using std::unordered_map.

Here is an example using std::deque:

#include <iostream>
#include <deque>

#define POOL_RESERVE 1000


// Data storage for your object
struct MyObjectData
{
    double some_data;
};


// Container returned by the factory
struct MyObject 
{
    typedef MyObjectData data_type;

    unsigned   id; 
    data_type* data;

    MyObject(): id(0), data(0) {}
    MyObject( const unsigned& id_, data_type* data_ ): id(id_), data(data_) {}

    void set( const unsigned& id_, data_type* data_ )
        { id = id_; data = data_; }
};


// MyObject Pool
class MyObjectPool
{
public:

    typedef MyObjectData data_type;

    MyObjectPool(): count(0) { pool.resize(POOL_RESERVE); }

    void get( const unsigned& id, MyObject& obj )
        {
            // Check requested index
            if ( id >= count )
                obj.set( 0, 0 );
            else
                obj.set( id, &pool[id] );
        }

    void create( MyObject& obj )
        {
            // Create new data container if needed
            if ( count++ >= pool.size() ) pool.emplace_back();

            // Return next available object
            obj.set( count-1, &pool[count-1] );
        }

private:

    unsigned count;
    std::deque<data_type> pool;
};


// MyObject factory
class MyObjectFactory
{
    typedef MyObjectFactory self;
    static MyObject local;

    static MyObjectPool& get_instance()
        {
            static MyObjectPool pool; 
            return pool;
        }

public:

    static MyObject get( const unsigned& id )
        {
            self::get_instance().get(id,local);
            return local;
        }

    static MyObject create()
        {
            self::get_instance().create(local);
            return local;
        }
};

// Define static variable
MyObject MyObjectFactory::local = MyObject();


// Usage example
int main()
{
    MyObject a,b,c;

    a = MyObjectFactory::create();
    b = MyObjectFactory::create();
    c = MyObjectFactory::get(1);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top