在向量中分配临时元素(知道最大数量)的最快方法?

时间:2015-05-29 16:43:37

标签: c++ performance vector std

在函数中我需要在向量中存储一些整数。该函数被多次调用。我知道它们不到10,但每次调用函数的数量都是可变的。有更好的表现有什么选择?

在示例中我发现了这个:

std::vector<int> list(10)
std::vector<int>::iterator it=list.begin();
unsigned int nume_of_elements_stored;

for ( ... iterate on some structures ... ){
    if (... a specific condition ...){
         *it= integer from structures ;
         it++;
         nume_of_elements_stored++;
     }
}

慢于:

std::vector<int> list;
unsigned int num_of_elements_stored(0);

for ( ... iterate on some structures ... ){
    if (... a specific condition ...){
         list.push_back( integer from structures )
     }
}
num_of_elements_stored=list.size();

3 个答案:

答案 0 :(得分:4)

我要在这里走一条非常不酷的路线。有被钉十字架的风险,我建议std::vector在这里不是那么好。例外情况是,如果你对内存分配器感到幸运,并通过分配器获得那个时间局部性,那么创建和销毁一堆通常无法提供的少量vectors

<强>等待!

在人们杀了我之前,我想说vector很棒,一般来说,它是最全面的数据结构之一。但是当你在一个紧密的循环中反复创建一堆小小的vectors时,你正在寻找这样的热点(希望有一个分析器),这就是这种直接用法的地方vector可以咬你。

问题在于它是一个堆分配的结构(基本上是一个动态数组),当我们处理这样的少量阵列时,我们真的想要使用经常缓存的内存我们可以在堆栈的顶部分配/免费这么便宜。

缓解此问题的一种方法是在重复调用中重复使用相同的向量。将其存储在外部来电者功能的范围内并通过引用传递,clear,进行push_backs,冲洗并重复。值得注意的是clear并没有释放向量中的任何内存,因此它保留了以前的容量(当我们想要重用相同的内存并播放时间局部性时,这里很有用)。

但在这里我们可以玩到那个堆栈。作为一个简化的例子(使用C风格的代码,它在C ++中不是非常犹太,甚至是异常安全的烦恼,但更容易说明):

int stack_mem[32];
int num = 0;
int cap = 32;
int* ptr = stack_mem;

for ( ... iterate on some structures ... )
{
    if (... a specific condition ...)
    {
         if (num == cap)
         {
             cap *= 2;
             int* new_ptr = static_cast<int*>(malloc(cap * sizeof(int)));
             memcpy(new_ptr, ptr, num * sizeof(int));
             if (ptr != stack_mem)
                  free(ptr);
             ptr = new_ptr;
         }
         ptr[num++] = your_int;
    }
}

if (ptr != stack_mem)
     free(ptr);

当然,如果你使用这样的东西,你应该将它正确地包装在一个可重用的类模板中,该模板进行边界检查,不使用memcpy,具有异常安全性,正式的push_back方法, emplace_back,copy ctor,move ctor,swap,可能是fill ctor,range ctor,erase,range erase,insert,range insert,size,empty,iterators / begin / end,使用placement new来避免需要复制赋值或默认ctor,等

解决方案在N <= 32时使用堆栈(可以使用适合您的常见需求的不同数字),然后在超出时切换到堆。这使得它可以有效地处理您的常见案例场景,但在N可能在某些病态情况下可能很大的情况下,也不仅仅是在那些罕见的情况下变得无用。这使得它在某种程度上可以与C中的可变长度数组相比(我实际上希望我们在C ++中有这样的东西,至少在std::dynarray可用之前),但没有堆栈溢出趋势,因为在极少数情况下它会切换到堆场景。

我将所有这些符合标准的形式与基于此想法的结构应用于接受<T, FixedN>的类模板,现在使用它几乎与vector一样多,因为我处理了这么多案例像这样,重复创建了很少的阵列,在绝大多数常见情况下,它应该适合堆叠(但总是具有那些极为罕见的特殊可能性)。它消除了许多我在地图上与内存相关的探查器热点。

......但是应用这个基本想法可能会给你带来很大的推动力。如果它在你的测量中得到回报,你可以将这种努力应用到将其包装到保存C ++对象语义的安全容器之上,并且我认为在你的情况下它应该相当多。

答案 1 :(得分:2)

我可能会选择中间立场:

std::vector<int> list;
list.reserve(10);

......其余的可能就像你的第二个版本。然而,说实话,这可能会让人怀疑这是否真的会产生很大的不同。

答案 2 :(得分:0)

如果使用静态向量,它将只分配一次。 第一个示例工作得更慢,因为它分配和销毁每个调用的向量。