假设我们有一个创建并使用可能很大的vector<foo>
的方法。
已知最大元素数为maxElems
。
据我所知,C++ 11的标准实践是:
vector<foo> fooVec;
fooVec.reserve(maxElems);
//... fill fooVec using emplace_back() / push_back()
但是,如果在大多数情况下调用我们的方法时,元素的数量将显着减少,那会发生什么情况呢?
除了多余的分配内存之外,保守的
reserve
调用是否还有其他缺点(如果需要,可以用shrink_to_fit()
释放这些内存)? 最佳答案
概括
使用太大的储备可能会有一些弊端,但是储备多少又取决于reserve()
的大小和上下文以及特定的分配器,操作系统及其配置。
您可能已经知道,在Windows和Linux之类的平台上,大的分配通常不会分配任何物理内存或页表项,直到它第一次被访问,因此您可能会想到大的未使用分配是“空闲的”。有时这称为“保留”内存而不“提交”内存,在此我将使用这些术语。
有一些原因可能使它不像您想象的那样免费:
页面粒度
上述的惰性提交仅在页面粒度下发生。如果您使用的是(典型)4096个字节的页面,则意味着如果您通常为一个 vector 保留4,000个字节,而该 vector 通常包含占用100个字节的元素,那么惰性提交不会为您带来任何好处!至少必须提交4096字节的整个页面,并且您不节省物理内存。因此,重要的不只是预期大小与保留大小之间的比率,而是保留大小的绝对大小决定了您将看到多少浪费。
请记住,许多系统现在都在透明地使用“大页面”,因此在某些情况下,粒度大约为2 MB或更大。在那种情况下,您需要大约10s或100s MB的分配才能真正利用惰性分配策略。
较差的分配性能
C++的内存分配器通常会尝试分配大块内存(例如,在类似Unix的平台上通过sbrk
或mmap
),然后将其有效地分解为应用程序所请求的小块。通过诸如mmap
之类的系统调用来获取这些大块内存可能比分配器中的快速路径分配(通常只有十几条指令)慢几个数量级。当您要求大块您通常不会使用的大块块时,您就会挫败这种优化,并且通常会走缓慢的道路。
举一个具体的例子,假设您的分配器向mmap
询问了128 KB的块,它将分配给分配的这些块。您在典型的vector
中分配了大约2K的内容,但reserve
为64K。现在,您将为每个其他mmap
调用支付一个reserve
调用,但是如果您只是要求最终需要2K,则mmap
调用将减少大约32倍。
对过量使用处理的依赖
当您需要大量内存而又不使用它时,您可能会遇到这样一种情况,即您需要的内存比系统支持的更多(例如,比RAM +交换的数量更多)。是否允许使用它取决于您的操作系统以及它的配置方式,并且如果您随后通过简单地编写更多内存来承担一些有趣的行为,则不管您打算做什么。我的意思是任意进程可能会被杀死,或者您在任何内存写入时都可能会遇到意外错误。由于overcommit tunables不同,在一个系统上有效的方法在另一个系统上可能会失败。
最后,由于监视工具报告的“VM大小”度量标准与流程最终提交的内容并没有太大关系,因此它使流程管理变得更加困难。
恶劣的地方
分配的内存超过了您的需要,可能会使您的工作集在虚拟地址空间中分布得更稀疏。总体效果是减少了引用位置。对于很小的分配(例如几十个字节),这可能会减少缓存行内的局部性,但是对于较大的分配,其主要作用可能是将您的数据分散到更多的物理页面上,从而增加了TLB压力。确切的阈值将在很大程度上取决于细节,例如是否启用了大页面。
关于c++ - Reserve()的高估是否有不利之处?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45331719/