Использование boost.pool вместо 'new' для контейнера объектов

В кодовой базе, над которой я работаю, в настоящее время есть код, который делает это часто:

// 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];
    // ......
}

Обычно каждый раз, когда строка изменяется в метке, мы удаляем старый набор буквенных объектов и создаем их заново. Эти буквенные объекты несколько легковесны, но так как это часто случается, я не могу просто использовать std::vector<Letter> так как каждый push_back() приведет к копии. Я бы тоже хотел избежать копии.

Поможет ли здесь использование Boost Pool? Я могу себе представить, как это сделать (это псевдокод, поскольку я пока не уверен, как точно использовать пул повышения):

// 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
    }

    // ......
}

Это позволит избежать копирования и избежать частого размещения. Тем не менее, я снизил удобство сопровождения кода, поскольку при добавлении / удалении элементов из вектора задействовано так много шаблонов.

Я думал об использовании boost::ptr_vector с пользовательским средством удаления, но не уверен, что это сильно поможет. Это помогает очистить, но я все еще должен позвонить pool::malloc() каждый раз, когда я делаю push_back.

Использование собственного распределителя с std::vector, по-видимому, также не имеет смысла, так как он в любом случае предварительно выделен и не будет уменьшаться в размере.

Может ли кто-нибудь помочь мне найти "лучшее" решение этой проблемы?

2 ответа

Я думаю, что пул памяти будет иметь значение в некоторых ситуациях. поскольку boost::object_pool<> не предоставляет метод для выделения массива объектов, поэтому я бы использовал boost::pool<> который на самом деле является основным пулом памяти 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);
};

Запустите на моей машине и coliru, он показывает, что чем чаще происходит выделение ресурсов, тем больше преимущество использования пула памяти.

То, что я думаю, я бы сделал, это использовать vector а также resize минимизировать количество выделений и разрешить повторное использование писем. Итак, у нас есть что-то вроде этого:

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

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

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

С таким подходом, как многие Letter объекты, насколько это возможно, повторно используются из предыдущего экземпляра. Вы даже можете позвонить reserve в Label конструктор для предварительного выделения достаточного пространства для предотвращения копирования / перемещения объектов Letter в дальнейшем.

Другие вопросы по тегам