std :: vector reserve()和push_back()比resize()和数组索引更快,为什么?

时间:2009-09-22 16:57:52

标签: c++ optimization stl vector

我正在对一段代码进行快速性能测试

void ConvertToFloat( const std::vector< short >& audioBlock, 
                     std::vector< float >& out )
{
    const float rcpShortMax = 1.0f / (float)SHRT_MAX;
    out.resize( audioBlock.size() );
    for( size_t i = 0; i < audioBlock.size(); i++ )
    {
        out[i]  = (float)audioBlock[i] * rcpShortMax;
    }
}

我很高兴原来非常天真的实现速度超过1毫秒来处理65536个音频样本。

然而,为了好玩,我尝试了以下

void ConvertToFloat( const std::vector< short >& audioBlock, 
                     std::vector< float >& out )
{
    const float rcpShortMax = 1.0f / (float)SHRT_MAX;
    out.reserve( audioBlock.size() );
    for( size_t i = 0; i < audioBlock.size(); i++ )
    {
        out.push_back( (float)audioBlock[i] * rcpShortMax );
    }
}

现在我完全希望这能提供与原始代码完全相同的性能。但突然之间,循环现在占用900usec(即它比其他实现快100usec)。

任何人都能解释为什么这会带来更好的表现吗? resize()是否初始化新​​分配的向量,其中reserve只分配但不构造?这是我唯一能想到的。

PS这是在单核2Ghz AMD Turion 64 ML-37上测试的。

4 个答案:

答案 0 :(得分:59)

  

resize是否初始化新​​分配的向量,其中reserve只分配但不构造?

答案 1 :(得分:4)

<强>调整大小()

修改容器,使其具有正好n个元素,在末尾插入元素或在必要时从末尾删除元素。如果插入了任何元素,则它们是t的副本。如果n > a.size(),则此表达式等同于a.insert(a.end(), n - size(), t)。如果n < a.size(),则相当于a.erase(a.begin() + n, a.end())

预订()

如果n小于或等于capacity(),则此调用无效。否则,它是分配额外内存的请求。如果请求成功,则capacity()大于或等于n;否则,capacity()不变。在任何一种情况下,size()都保持不变。

如果向量中插入了超过capacity() - size()个元素,则会自动重新分配内存。重新分配不会更改size(),也不会更改向量的任何元素的值。但是,它会增加capacity()

预留会导致手动重新分配。使用reserve()的主要原因是效率:如果您知道向量必须最终增长的容量,那么一次分配该内存通常更有效,而不是依赖于自动重新分配方案。

答案 2 :(得分:3)

第一个代码写入out[i],归结为begin() + i(即添加)。第二个代码使用push_back,它可能会立即写入等同于end()的已知指针(即没有添加)。你可以通过使用迭代器而不是整数索引来使第一次运行与第二次运行一样快。

编辑:也澄清了一些其他注释:向量包含浮点数,构造浮点数实际上是一个无操作符(同样地,声明“float f;”不会发出代码,只有告诉编译器为堆栈上的浮动节省空间)。所以我认为浮点数向量的resize()reserve()之间的任何性能差异都与构造无关。

答案 3 :(得分:1)

out.resize( audioBlock.size() );

由于out的大小(= 0)小于audioBlock.size(),因此会创建其他元素并将其附加到out的末尾。这通过调用它们的默认构造函数来创建新元素。

预留仅分配内存。