用于更快地创建新对象的C ++ Block Allocator

时间:2014-09-15 15:16:20

标签: c++ memory memory-management allocator

我有一段代码可以创建数千个对象,并将它们附加到向量中。 下面的代码只是正在做的事情的一个例子,即使构造函数有一些参数,并且for实际上没有那个条件,但它的目的是显示它运行了数千次。

vector<VolumeInformation*> vector = vector<VolumeInformation*>();
for (int i = 0; i < 5000; ++i) {
    VolumeInformation* info = new VolumeInformation();
    vector.push_back(info);
}

代码需要花费大量时间才能运行,我试图找到一种更快的方法来创建所有对象。我读到了有关块分配器的内容,但我不确定这是否真正意味着我要做的事情,以及它是否真的有助于更快地完成这项工作。我想为一千个对象分配内存(例如),并在它仍然可用时继续使用该内存,然后在需要时分配更多内存,避免每次都为单个对象分配内存。可以这样做吗?你能指点我到哪里可以找到一个关于如何告诉'new'使用以前分配的内存的例子吗?如果不是对象本身,可以将分配器用于向量的内存(即使对象真的需要加速)吗?

谢谢。

**更新**

在所有答案和评论之后,我决定对代码进行更改,因此向量将存储对象而不是指针,因此我可以使用reserve为向量预先分配一些内存,从而可以节省一些通过一次为多个对象实例分配内存的时间。虽然在做了一些性能基准之后,我确认我所做的更改表现得更糟,除非我提前知道向量的确切大小。以下是我的调查结果,我想知道是否有人可以阐明这一点,让我知道为什么会发生这种情况,如果我在这里遗漏了什么,或者我以前使用的方法是否真的是最好的。

以下是我用于基准测试的代码:

    vector<int> v = vector<int>();
    v.push_back(1);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    v.push_back(7);
    v.push_back(9);

    int testAmount = 200000;
    int reserve = 500000;

    Stopwatch w = Stopwatch();





    w = Stopwatch();
    vector<VolumeInformation> infos = vector<VolumeInformation>();
    infos.reserve(reserve);
    for (int i = 0; i < testAmount; ++i) {
        infos.emplace_back(&v, 1, 0, 0);
    }
    int elapsed = w.Elapsed();






    w = Stopwatch();
    vector<VolumeInformation*> infoPointers = vector<VolumeInformation*>();
    infoPointers.reserve(reserve);
    for (int i = 0; i < testAmount; ++i) {
        infoPointers.emplace_back(new VolumeInformation(&v, 1, 0, 0));
    }
    int elapsed2 = w.Elapsed();

如果我注释掉两个reserve()行,没有指针的版本需要32.701秒,而指针版本需要6.159!它比使用对象矢量少了5倍。

如果我使用reserve,但将要保留的项目数量设置为低于迭代次数的值,则对象向量版本仍然需要比指针版本更多的时间。

如果我使用值大于或等于迭代量的reserve,则对象版本的向量变得快得多,仅需要270ms,而指针版本只需8.901秒。这里的主要问题是我事先并不知道向量将达到的大小,因为迭代不是基于硬编码的数字,这只是为了进行基准测试。

有人可以解释为什么会发生这种情况,如果有另一种解决方法,或者我在这里做错了什么?

2 个答案:

答案 0 :(得分:2)

你可能希望在循环之前为你的5000个元素提供reserve空间:

vector.reserve(5000);
for (int i = 0; i < 5000; ++i) {
    VolumeInformation info = new VolumeInformation();
    vector.push_back(info);
}

这可以通过消除多个resize来节省时间,因为vector增长,如果VolumeInformation需要花费很多时间(及时)来复制。

答案 1 :(得分:2)

vector完全能够预先分配大块并将其用于所有元素,如果你只是正确使用它:

// create 5000 default-constructed X objects
std::vector<X> v(5000);

或者如果你需要传递构造函数参数:

std::vector<X> v;
v.reserve(5000);    // allocate block of memory for 5000 objects
for (int i=0 ; i < v.size(); ++i)
  v.emplace_back(arg1, arg2, i % 2 ? arg3 : arg4);

最后一行在预先分配的内存中构造X,没有复制,将函数参数传递给X构造函数。

  

我想为一千个对象分配内存(例如),并在它仍然可用时继续使用该内存,然后在需要时分配更多内存,避免每次都为单个对象分配内存。

std::vector自动执行此操作,您可能应该停止使用new并且只有vector<VolumeInformation>并直接将对象放入其中,而不是分配单个对象并存储指针。< / p>

内存分配很慢(请参阅Why should C++ programmers minimize use of 'new'?),因此请停止分配单个对象。上面的两个示例都将执行 1 分配和5000个构造函数调用。您的原始代码执行至少5001次分配和5000次构造函数调用(在典型的C ++实现中,它将执行 5013分配和5000次构造函数调用)。

** 更新 **

  

如果我注释掉两个reserve()行,没有指针的版本需要32.701秒,而指针版本需要6.159!它比使用对象矢量少了5倍。

由于您实际上没有显示完整的工作程序,因此您要求人们猜测(始终显示实际代码!)但它表明您的类具有非常慢速复制构造函数,这是当向量增长并且需要将现有元素复制到新内存时(旧元素随后被销毁)使用。

如果你可以添加一个比复制构造函数更高效的noexcept移动构造函数,那么当向量需要增长并且运行速度更快时,std::vector将使用它。

  

这里的主要问题是我事先并不知道向量将达到的大小,因为迭代不是基于硬编码的数字,这只是为了进行基准测试。

您可以保留比您可能需要的更多元素,更高的内存使用率以获得更好的性能。