当我们push_back元素时,std::vector 什么时候会放大自己?

When does a std::vector enlarge itself when we push_back elements?

本文关键字:什么时候 vector 放大 自己 std push 我们 back 元素      更新时间:2023-10-16

根据下面的测试,似乎std::vector<int>以这种方式增加其容量:

  • 当我们push_back()并且容量已经满时(即v.size() == v.capacity()(,必须注意的是,它不会在之前发生一点点

  • 容量增加到以前容量的 1.5 倍

问题:为什么是这个1.5因素?它是否依赖于实现?它是最佳的吗?

另外,有没有办法在此代码中分析何时发生重新分配?(有时也许可以在不移动阵列第一部分的情况下增加容量(


vector<int> v;
int previouscapacity = 0;
for (unsigned int i = 0; i < 1000000; i++)
{
v.push_back(i);
if (v.capacity() != previouscapacity)
{
wcout << L"new capacity: " << v.capacity() << L" new size: " << v.size() << L" ratio: " << ((float) v.capacity()) / previouscapacity << 'n';
previouscapacity = v.capacity();
}
}
新容量:1 新容量:1 比率

:1.#INF
新容量:2 新容量:2 新容量:3
比率:1.5
新容量:4 新容量:4 比率:1.33333
新容量:6 新容量:5 比率:1.5
新容量:9 新尺寸:7 比率:1.5
新容量:13 新容量:10 比率: 1.44444 新容量: 19 新容量: 14 比率: 1.46154 新容量: 28 新容量: 20 比率: 1.47368 新容量: 42 新容量: 29 比率: 1.5 新容量: 63 新容量: 43 比率: 1.5 新容量: 94 新容量:
64 比率:



1.49206 新容量:
141 新容量: 95 比率: 1.5 新容量:
211 新容量: 142 比率: 1.49645
...
新容量:466609 新容量:311074比率:1.5
新容量:699913 新容量:466610 比率:1.5
新容量:1049869 新容量:699914比率:1.5


注意:我正在使用VC++ 2013

喜欢对链接问题的回答 动态分配的阵列的理想增长率是多少? 显示,总是将分配的大小加倍存在一个问题,即释放的内存对于下一次分配来说总是太小了。矢量将在堆中"徘徊",留下大量碎片。

最大化重用的"最佳"重新分配大小原来是黄金比例 1.61803...

但是,1.5 作为capacity() + capacity() / 2计算要容易得多,并且在实践中足够接近。这使其成为现有实现的热门选择。

另外,有没有办法在此代码中分析何时发生重新分配? 当您向后推并且没有足够的容量时,就会发生这种情况。 没有什么可分析的。

为什么是这个1.5因素?它是否依赖于实现?它是最佳的吗?

增长因子必须是指数级的。 它不必是统一的。 2很受欢迎,因为它导致"平均每个元素被复制一次"。

2 的问题在于,无论序列有多长,具有重新分配的行进向量永远不会为下一次重新分配留出足够的空间来拟合,如 1+2+4+8 <32。 1.5、1+1.5+2.25+3.3> 7.7,因此从长远来看,以前释放的空间足以容纳新需要的空间。

如果不断增长的向量是内存的主要消耗者,则在 2 倍缩放的情况下,剩余的旧向量缓冲区将丢弃一半的内存。 在 1.5 以下,丢弃的缓冲区会增长,并最终变得足够大,可以回收以进行新的分配(假设它们是连续的(。

系数 1.5 由Microsoft的 STL 实施团队决定。根据它们,1.5 到 2 的系数是速度和内存优化的最佳解决方案。
您可以在此处查看说明,从第 15 分钟开始。

我认为为什么因子为 1.5 比 2.0 更受欢迎,答案的一个重要方面是标准 c++ 分配器模型不支持realloc函数:

重新分配标准分配器

如果它存在,并且它将用于 std::vector,那么在因子 1.5 优于例如因子 2.0 容量增加的所有情况下,提到的内存碎片问题将最小化。

当缓冲区始终在每次缓冲区大小增加时重新定位时,1.5 的因子(或更准确地说是黄金比例(是最佳的(如其他答案所解释的那样(。

使用 realloc,如果当前内存块之后有空间,它只会在不复制的情况下放大。如果没有空间,realloc 调用当然会移动数据块。但是,自由块(如果这种情况发生多次(无论如何都不会是连续的。那么 1.5 还是 2 是缓冲区扩大的因素就没关系了......无论如何,内存是支离破碎的。

因此,在 std::vector 中采用realloc方法将有助于因子 1.5 优于 2.0保存数据副本。

有些人称 c++ 分配器中缺少realloc功能是设计缺陷(见链接(,我倾向于同意这里。

为什么C++分配器中没有重新分配功能?