为什么C ++ STL向量在做多个预留时会慢1000倍?

时间:2018-01-31 06:23:04

标签: c++ vector stl resize

我遇到了一个奇怪的情况。

在我的程序中,我有一个循环,它将一堆数据组合在一个巨大的向量中。我试图弄清楚为什么它运行得如此缓慢,尽管我似乎正在尽一切努力在旅途中以有效的方式分配内存。

在我的程序中,很难确定组合数据的最终向量应该有多大,但是每个数据的大小在处理时都是已知的。因此,在一次性使用保留调整大小组合数据向量时,我为每个数据块保留了足够的空间,因为它被添加到较大的向量中。那时我遇到了这个问题,可以使用下面的简单片段重复:

std::vector<float> arr1;
std::vector<float> arr2;
std::vector<float> arr3;
std::vector<float> arr4;
int numLoops = 10000;
int numSubloops = 50;

{
    // Test 1
    // Naive test where no pre-allocation occurs

    for (int q = 0; q < numLoops; q++)
    {
        for (int g = 0; g < numSubloops; g++)
        {
            arr1.push_back(q * g);
        }
    }
}

{
    // Test 2
    // Ideal situation where total amount of data is reserved beforehand

    arr2.reserve(numLoops * numSubloops);
    for (int q = 0; q < numLoops; q++)
    {
        for (int g = 0; g < numSubloops; g++)
        {
            arr2.push_back(q * g);
        }
    }
}

{
    // Test 3
    // Total data is not known beforehand, so allocations made for each
    // data chunk as they are processed using 'resize' method

    int arrInx = 0;
    for (int q = 0; q < numLoops; q++)
    {
        arr3.resize(arr3.size() + numSubloops);
        for (int g = 0; g < numSubloops; g++)
        {
            arr3[arrInx++] = q * g;
        }
    }
}

{
    // Test 4
    // Total data is not known beforehand, so allocations are made for each
    // data chunk as they are processed using the 'reserve' method

    for (int q = 0; q < numLoops; q++)
    {
        arr4.reserve(arr4.size() + numSubloops);
        for (int g = 0; g < numSubloops; g++)
        {
            arr4.push_back(q * g);
        }
    }
}

在Visual Studio 2017中编译后,此测试的结果如下:

Test 1: 7 ms
Test 2: 3 ms
Test 3: 4 ms
Test 4: 4000 ms

为什么运行时间存在巨大差异?

为什么多次调用reserve,然后调用push_back比调用resize多一倍,然后进行直接索引访问?

它有什么意义,它可能比不包括预分配的天真方法花费500倍的时间?

2 个答案:

答案 0 :(得分:18)

  

它是如何理解它可能需要500倍的时间   天真的方法,根本不包括预先分配?

你错了。天真的&#39;你说的方法是做预分配。他们只是在幕后完成,而且很少在push_back的电话中完成。每次拨打push_back时,它都不会为一个元素分配空间。它分配的数量是当前容量的一个因子(通常在1.5倍和2倍之间)。然后它不需要再次分配,直到容量耗尽。这比你的循环更有效,每次添加50个元素时都会进行分配,而不考虑当前的容量。

答案 1 :(得分:4)

@Benjamin Lindley的回答解释了std::vector的能力。但是,正是为什么第四个测试用例很慢,实际上它是标准库的实现细节。

[vector.capacity]

  

void reserve(size_type n);

     

...

     

效果:一种指令,通知向量计划的大小更改,以便它可以相应地管理存储分配。 在reserve()之后,如果重新分配发生,capacity()大于或等于reserve的参数;并且等于capacity()的先前值。当且仅当当前容量小于reserve()的参数时,才会发生重新分配。

因此,C ++标准保证,在reserve()之后,对于更大的容量,实际容量应该是请求的容量。我个人认为,当收到这样大容量的请求时,实施遵循某些特定政策并非不合理。但是,我也在我的机器上进行了测试,似乎STL只做最简单的事情。