shrink_to_fit是将' std::vector '的容量缩减到它的大小的正确方法吗?
Is shrink_to_fit the proper way of reducing the capacity a `std::vector` to its size?
在c++ 11中引入了shrink_to_fit
来补充某些STL容器(如std::vector
, std::deque
, std::string
)。
synpsizing,它的主要功能是请求与相关联的容器减小其容量以适应的大小。然而,这个请求是非绑定的,容器实现可以自由地进行优化,从而使vector的容量大于其大小。
此外,在之前的SO问题中,OP被劝阻使用shrink_to_fit
来减少std::vector
的容量到它的大小。不这样做的理由如下:
shrink_to_fit
什么都不做,或者它给你缓存局部性问题,它是O(n)到执行(因为你必须把每件物品复制到它们新的、更小的地方)。通常在内存中保留空闲会更便宜。@Massa
谁能回答以下问题?
- 引号中的参数是否成立?
-
如果是,缩小STL容器容量的正确方法是什么(至少对于
std::vector
)。 - 如果有更好的方法来收缩容器,
shrink_to_fit
存在的原因到底是什么?
引语中的参数是否成立?
测量一下,你就知道了。你的内存受限吗?你能预先算出正确的尺寸吗?使用reserve
比使用收缩更有效。总的来说,我倾向于同意这样一个前提,即大多数用途可能对slack没有问题。
如果是,如何将STL容器的容量缩小到它的大小(至少对于std::vector)
注释不仅适用于shrink_to_fit
,也适用于任何其他方式的收缩。考虑到你不能就地realloc
,它涉及到获取一个不同的内存块并复制到那里,而不管你使用什么机制来收缩。
如果有更好的收缩容器的方法,那么shrink_to_fit存在的原因到底是什么呢?
请求是非绑定的,但是替代方案没有更好的保证。问题是收缩是否有意义:如果有意义,那么提供一个shrink_to_fit
操作是有意义的,该操作可以利用对象被移动到到新位置的事实。也就是说,如果类型T
有noexcept(true)
move构造函数,它将分配新的内存并移动元素。
虽然您可以在外部实现相同的功能,但此接口简化了操作。在c++ 03中相当于shrink_to_fit
的是:
std::vector<T>(current).swap(current);
但是这种方法的问题是,当对临时对象进行复制时,它不知道current
将被替换,没有任何东西告诉库它可以移动持有的对象。注意,使用std::move(current)
不能达到预期的效果,因为它将移动整个缓冲区,保持相同的capacity()
。
在外部实现这个会有点麻烦:
{
std::vector<T> copy;
if (noexcept(T(std::move(declval<T>())))) {
copy.assign(std::make_move_iterator(current.begin()),
std::make_move_iterator(current.end()));
} else {
copy.assign(current.begin(), current.end());
}
copy.swap(current);
}
假设我得到了正确的if条件…这可能不是您每次想要执行此操作时都想要编写的内容。
- 参数是否成立?
由于这些论点最初是我的,如果我为它们辩护,请不要介意:
-
shrink_to_fit
不做任何事情(…)如前所述,标准说(很多次,但在
vector
的情况下是23.3.7.3节…)请求是非绑定的,以允许优化的实现自由度。这意味着实现可以将shrink_to_fit
定义为no-op -
(…)或者它给你缓存局部性问题
在
shrink_to_fit
不是作为无操作实现的情况下,您必须分配一个容量为size()
的新底层容器,复制(或者,在最好的情况下,移动)从旧容器中构造所有N = size()
新项目,销毁所有旧项目(在移动情况下,这应该被优化,但这可能涉及到旧容器的循环),然后销毁旧容器本身。这是在libstdc++-4.9
中完成的,正如David Rodriguez所描述的那样,通过_Tp(__make_move_if_noexcept_iterator(__c.begin()), __make_move_if_noexcept_iterator(__c.end()), __c.get_allocator()).swap(__c);
和
libc++-3.5
中,__alloc_traits
中的一个函数做了大致相同的事情。哦,实现绝对不能依赖于
realloc
(即使它在::operator new
中使用malloc
来分配内存),因为realloc
,如果它不能就地收缩,要么不占用内存(无操作情况),要么进行位复制(并错过重新调整指针的机会,等等,适当的c++复制/移动构造函数会给)。当然,可以编写一个可收缩内存分配器,并在其向量的构造函数中使用它。
在向量大于缓存线的简单情况下,所有的移动都会给缓存带来压力。
-
是O(n)
如果
n = size()
,我认为上面已经建立了,至少,你必须做一个n
大小的分配,n
复制或移动构造,n
销毁,和一个old_capacity
大小的释放。 -
通常把空闲时间留在内存中比较便宜
显然,除非你真的需要空闲内存(在这种情况下,将数据保存到磁盘并在需要时重新加载它可能更明智…)
- 如果是,如何将STL容器的容量缩小到其大小(至少对于std::vector)。
正确的方法仍然是shrink_to_fit
…您只需要要么不依赖它,要么非常了解您的实现!
- 如果有更好的收缩容器的方法,那么shrink_to_fit存在的原因是什么?
没有更好的方法,但是shrink_to_fit
存在的原因是,AFAICT,有时你的程序可能会感到内存压力,这是一种处理它的方法。不是一个很好的方法,但仍然。
HTH !
- 如果是,如何将STL容器的容量缩小到其大小(至少对于std::vector)。
'swap技巧'将把向量修剪到所需的确切大小(来自More Effective STL):
vector<Person>(persons).swap(persons);
在vector为空时特别有用,用于释放所有内存:
vector<Person>().swap(persons);
由于保留未使用空间的分配,向量不断地触发我的单元测试器的内存泄漏检测代码,并且这将它们完美地分类。
这是一个我并不关心运行时效率(大小或速度)的例子,但我确实关心确切的内存使用情况。
- 如果有更好的收缩容器的方法,那么shrink_to_fit存在的原因是什么?
我真的不知道提供一个可以合法地做任何事情的函数有什么意义。当我看到它被引入时,我欢呼雀跃,当我发现它不可靠时,我绝望了。
也许我们会在下一个版本中看到maybe_sort()
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 当矢量改变容量时,有什么方法可以更新指针/参考值
- 制作 std::矢量容量>=N 和大小=0 的最佳方法是什么?
- "Safe"将元素添加到 std::vector 的方法,超出了其 size() 但低于其容量 ()
- 初始化矢量<矢量的最快方法>大小为 0,容量<double>固定
- shrink_to_fit是将' std::vector '的容量缩减到它的大小的正确方法吗?