Frage

In the code base I'm working on, it currently has code that does this often:

// In the header:
class Label
{
public:
    void ParseText();

private:
    Letter* m_myArray;
};

// In the CPP:
void ParseText()
{
    delete[] m_myArray;
    m_myArray = new Letter[string_length];
    // ......
}

Basically every time the string changes in the label, we delete the old set of letter objects and create them all over again. These letter objects are somewhat lightweight, but since this happens often I can't simply use std::vector<Letter> since each push_back() would result in a copy. I'd like to avoid the copy too.

Would using boost pool help here? I can imagine doing this (this is pseudocode, since I'm not sure how to use boost pool exactly yet):

// In the header:
class Label
{
public:
    void ParseText();

private:
    std::vector<Letter*> m_myArray;
    boost::object_pool m_pool;
};

// In the CPP:
void ParseText()
{
    // Loop through each element in m_myArray and call pool::free
    m_myArray.clear();

    // Loop each letter and create a new Letter object in the container
    for( ... ) {
        m_myArray.push_back(m_pool.malloc()); // Not sure how to handle constructor params
    }

    // ......
}

This would avoid the copy and would avoid doing allocations so often. However, I've lowered the maintainability of the code since there is so much boilerplate involved in adding/deleting items from the vector.

I've thought of using boost::ptr_vector with a custom deleter, but not sure if this helps much. It helps cleanup but I still have to call pool::malloc() each time I do a push_back.

Using a custom allocator with std::vector doesn't seem to make sense either since it's preallocated anyway and won't shrink in size.

Can anyone help me figure out the "best" solution for this problem?

War es hilfreich?

Lösung

I think a memory pool would make a difference in some situations. Since boost::object_pool<> doesn't provide a method to allocate a array of objects, so I would use boost::pool<> which is actually the underlying memory pool of boost::object_pool<>.

#include <cstdio>
#include <ctime>
#include "boost/pool/pool.hpp"

struct Letter{
    float a, b, c;
    int *p;
};

class Label
{
public:
    Label() : m_myArray(NULL), string_length(1), last_size(0){}

    void set_size(size_t n)
    {
        last_size = string_length; // use last_size to store the number of last allocation, just for test.
        string_length = n;
    }
    void ParseText()
    {
        delete[] m_myArray;
        m_myArray = new Letter[string_length];
    }

    void ParseText_pool();

private:
    Letter* m_myArray;
    size_t string_length;
    size_t last_size; //boost::pool<>::ordered_free need the size
};

boost::pool<> p(sizeof(Letter));

void Label::ParseText_pool()
{
    if(m_myArray)
        p.ordered_free(m_myArray, last_size); // ordered_free need the right size
    m_myArray = (Letter*)p.ordered_malloc(string_length); // if you need call the ctor, use placement new.
}

int main()
{
    Label l;

    float startTime = (float)clock()/CLOCKS_PER_SEC;

    for(int i = 1; i < 1000000; ++i)
    {
        l.set_size(i%100 + 1);
        l.ParseText();
    }

    float endTime = (float)clock()/CLOCKS_PER_SEC;

    printf("without pool, time: %f\n", endTime - startTime);

    Label l2;
    startTime = (float)clock()/CLOCKS_PER_SEC;

    for(int i = 1; i < 1000000; ++i)
    {
        l.set_size(i%100 + 1);
        l2.ParseText_pool();
    }

    endTime = (float)clock()/CLOCKS_PER_SEC;

    printf("with pool, time: %f\n", endTime - startTime);
};

Run on my machine and coliru, it shows the more frequent the allocation, the greater the advantage of using a memory pool.

Andere Tipps

What I think I would do is use vector and resize to minimize the amount of allocations, and allow letters to be reused. So we have something like this:

// In the header:
class Label
{
public:
    void ParseText();

private:
    std::vector<Letter> m_myArray;
};

// In the CPP:
void ParseText()
{
    m_myArray.resize(string_length);
    // ......
}

With an approach like this, as many Letter objects as possible are reused from the previous instance. You can even call reserve in the Label constructor to pre-allocate enough space to even prevent any copy/movement of the Letter objects later.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top