1

在我正在处理的代码库中,它目前有经常这样做的代码:

// 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 的自定义分配器似乎也没有意义,因为它无论如何都是预先分配的并且不会缩小大小。

谁能帮我找出解决这个问题的“最佳”解决方案?

4

2 回答 2

0

我认为内存池在某些情况下会有所作为。由于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上运行,它显示分配越频繁,使用内存池的优势就越大。

于 2014-04-23T03:03:14.097 回答
0

我想我会做的是使用vectorresize最小化分配量,并允许重用字母。所以我们有这样的事情:

// 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对象从前一个实例中被重用。您甚至可以reserveLabel构造函数中调用以预先分配足够的空间,甚至可以防止以后对 Letter 对象进行任何复制/移动。

于 2014-04-22T15:21:29.247 回答