deque :: shrink_to_fit内存保证

时间:2014-06-01 13:47:03

标签: c++ c++11 deque

我尝试在从deque开头删除范围后使用deque::shrink_to_fit,因为我有严格的内存要求。但是,它没有工作,我只是看到libstdc ++使用交换技巧和副本实现shrink_to_fit。这实际上意味着,而不是更好的内存使用,我得到2倍的使用一段时间,并因此得到OOM编辑。

我认为这限制了shrink_to_fit的可用性,我想知道标准中是否有/可以有任何保证?我在草稿副本(N3035)中查找了它,只看到了#34;这是一个非约束性的请求......"。我知道它为什么不具有约束力,以及它为什么不能为vector完成,但是根据我对deque实现的了解,应该可以给出一些内存保证(看看libc ++,它似乎以更聪明的方式做到了。是否有保证,因为它们与具体实施有关?

1 个答案:

答案 0 :(得分:7)

shrink_to_fit的libstdc ++和libc ++实现看起来都符合我的要求。但它们非常不同,主要是因为这两个实现遵循不同的类不变量。

首先,对于那些不了解的人来说,std::deque是一个指针数组(通常是T*,但是自定义分配器可以将其推广到)固定大小的{{1}数组1}}。指针数组可以被视为循环缓冲区,或者作为缓冲区,可以将其数据的开头从缓冲区的起始处滑开。

这个答案将集中在deque的固定长度数组上。

<强>的libstdc ++

libstdc ++实现有一个不变量,即T中永远不会有空数组。如果dequepop_front创建一个空数组,则在擦除期间将释放该数组。这种设计减少了pop_back实际可以实现的内容,因为deque::shrink_to_fit始终处于或非常接近最小内存占用量。

libstdc ++ deque执行副本&amp;只有当它可以证明它可以通过这样做来消除数组时才进行交换。例如,shrink_to_fit可以保存由三个固定长度数组支持的1010个值,每个数组的容量为1000个值。 deque中的第一个元素可能从第一个数组中的第995位开始。因此,第一和第三阵列中的大多数(但不是全部)都是空的。复制/交换将分配两个新数组,将1010个元素复制/移动到这两个数组中,然后解除分配旧的3个数组。

<强>的libc ++

libc ++实现遵循略微不同的设计,旨在加速FIFO queues。当deque(或pop_front)清空数组时,除非前面(或后面)已有空数组,否则不会释放该数组。即而libstdc ++不变量是整个pop_back中从不存在空数组,而libc ++不变量是deque的前后可以有零个或一个空数组。这样做的理由是,如果deque(或push_back)需要一个新数组,它首先会在push_front的另一端查找一个空数组,并从那里进行处理,在使用分配新数组之前。给定具有近似恒定大小的FIFO队列,该设计将达到使deque / pop_front序列永远不会分配新阵列的状态。相反,数组从push_back的前面再循环到后面(反之亦然)。

libc ++ deque将丢弃shrink_to_fit两端的空数组(如果存在)。相比之下,libstdc ++不需要这样做,因为空数组永远不存在。 libc ++ deque并不试图通过&#34;转移&#34;来进一步减少内存占用。通过复制/交换操作将值更接近块的开头。

结果是libc ++ shrink_to_fit永远不会使引用无效,而libstdc ++ shrink_to_fit经常会。请注意,shrink_to_fit的规范旨在允许引用无效,但现在我看,我认为它错误地没有(它必须用于shrink_to_fit,并且{{1}的措辞取自vector::shrink_to_fit)的措辞。

两个实现也将deque vector的底层缓冲区,尽管这会产生相对较小的影响,因为shrink_to_fit缓冲区的内存占用量通常比内存占用空间小得多数组。每个libc ++数组通常占用4096个字节(1页),而每个libstdc ++数组通常占用512个字节。

我不知道VC ++ T*做了什么。但它也会实现一组数组。

可以看出,虽然所有实现都在相同的数据结构上运行(为了符合复杂性和无效要求),但仍有一些重要的设计决策留给实现,每个实现都努力提供它认为对客户最好的东西。