每个循环迭代都清除向量。最节省内存的方法是什么?

5

我有一个关于std::vector的问题。

我有一个非常消耗内存的算法,我预见到提前预测向量大小并为向量保留足够的内存将有助于减少内存使用。

以下哪种方式更好:

for ( ... ) {
  std::vector<Type> my_vector;
  my_vector.reserve(stuff_count);
  // Do stuff , and append stuff to my_vector.
}

或者是这个:
std::vector my_vector;
for ( ... ) {
  my_vector.clear();
  my_vector.reserve(stuff_count);
  // Do stuff , and append stuff to my_vector.
}

请告诉我哪种方法最好,或者是否有更好的方法来完成工作。
非常感谢您提前的帮助!

这不是一种观点类型的问题。你应该测量你特定系统的差异。 - Martin York
9个回答

11

第一种方案每次迭代都要重新分配向量的缓冲区,这通常是相当昂贵的。第二种方案只偶尔重新分配。因为速度是你的优先考虑因素,所以第二种方案更好。

从你的问题中不清楚元素数量从何而知。也许你甚至可以快速计算出所有迭代的最大元素数,将其设置为缓冲区大小,并且无需重新分配。


通常这是相当昂贵的:如果没有进行分析,这就是一个大胆的陈述。C++内存分配器正是为这种情况而设计的,所以我对此表示怀疑(但即使如此,这种说法也是大胆的,需要经过测试)。 - Martin York
好的,我们曾经在类似的情况下进行过性能剖析。在极端情况下,这种重新分配大约占用了95%的时间——分配了一个缓冲区,复制数据,简要分析数据,然后丢弃缓冲区。那真是令人吃惊。 - sharptooth
这就导致了一个结论,即如果一个人想要快速执行,他应该避免不必要的重新分配。 - sharptooth
@sharptooth - 如果你只是分析传递的缓冲区中保存的数据,为什么不通过引用传递该数据呢? - MacX.dmg
@ MacX.dmg - 这正是我们为解决问题所做的。但在进行分析之前,没有人预料到简单复制一个小数组会带来如此大的惩罚。 - sharptooth

6

我预见到提前预测向量大小并预留足够的内存将有助于大幅减少内存使用。

尽量像工程师一样而不是占卜师。创建一个测试,然后衡量差异。


5
第一个更简洁,第二个可能略微快一些。

清理是可以的,但在我的情况下,清理并不重要,因为我的算法需要尽可能多的优化。我的未经优化的测试可以轻松使用10GB的内存,没有问题。 - Nailer
那么你就不应该问这样的问题(每个人的意见可能都不同/错误)。你应该做的是对代码进行分析,并在较小的数据集上计时。 - Martin York

5

由于代码的差异微不足道,为什么不尝试测试这两种方法,并查看哪种最适合您特定的应用程序呢?


1

这取决于Type需要如何构造/析构,我想说。如果它是一个不需要销毁的POD,您可以跳过clear(),它会在循环中调用所有的析构函数,并将其用作静态数组:

std::vector<Type> my_vector(size);
for (...)
{
  int index = 0;
  // Do stuff
  my_vector[index] = some_value;
}

(注意:代码未经测试)


如果它是一个POD,我期望向量在任何情况下都会优化销毁。在对性能做出结论之前,请先测试一下。 - jalf
从理论上讲,你是正确的,jalf。把我的答案看作是一个提示,你可能会破坏很多东西,那里可能有一些工作可以做。 - Carl Seleborg

1
预先为向量保留足够的内存将有助于我大大减少内存使用量。咦...什么?!这根本没有任何意义。预留内存并不能以任何方式帮助减少内存使用。它可以防止不断重新分配内存,从而使事情更快,但就使用而言,您不会获得任何好处。

实际上,可用的空闲字节数量将是相等的,但最大可分配的块将会更小:反复重新分配直到得到正确大小将最终导致堆的碎片化。最终结果将是这样。 - xtofl
此外,向量必须具有连续的内存,因此如果当前向量无法再扩展,就需要分配一个全新的块,复制并释放旧向量。这暂时会使用(oldsize + newsize)的内存。如果您有巨大的内存需求,这将变得相关。 - Pieter
向量在调用附加函数时会分配所需内存的两倍,如果没有足够的空间来容纳下一个元素。这意味着,如果我有一个刚好能容纳100个元素的向量,当我添加第101个元素时,向量将分配足够200个元素的内存。 - Nailer
没错,说得好。我没有考虑到碎片化问题。无论如何,我都看不出上述任何一种实现方式能够解决这个问题。我也没有看到自定义分配或内存处理,所以问题仍然存在。我的观点仍然是正确的 ;) - OJ.

1

第二个将通过循环使用的所有用法中最大的内存,即 stuff_count 类型的最大大小。std::vector::clear() 并不一定释放内存。也就是说,如果在 std::vector::clear() 前后调用 std::vector::capacity(),符合标准的实现可能会返回相同的值。

最好情况下,您将使用上述方案减少分配内存的次数。但在任何时候您都不会减少内存占用量。如果要缩小到所保留的内存量,应该使用向量交换习惯用语:

std::vector<type>().swap(my_vector);
my_vector.reserve(stuff_count); 

或者你的第一个解决方案,因为整体效果将是相同的。

0
如果您需要执行大量的附加操作,请使用std::deque而不是std::vector,并只使用“push_back”。

0
怎么样?
std::vector<DataType> my_vector;
my_vector.resize(sizeof(yourData));
memcpy(reinterpret_cast<char*>(&my_vector), &yourData, sizeof(yourData));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接