17

std::realloc如果 malloc 的内存包含非 pod 类型,则在 c++ 中是危险的。似乎唯一的问题是,std::realloc如果它不能原地增加内存,就不会调用类型析构函数。

一个微不足道的工作将是一个try_realloc函数。如果它不能就地增长,它不会分配新内存,而是简单地返回 false。在这种情况下,可以分配新内存,将对象复制(或移动)到新内存,最后释放旧内存。

这似乎非常有用。 std::vector可以充分利用这一点,可能避免所有副本/重新分配。
抢先式阻燃剂:从技术上讲,这与 Big-O 性能相同,但如果向量增长是您应用程序中的瓶颈,那么即使 Big-O 保持不变,x2 的加速也是不错的。

但是,我找不到任何像try_realloc.

我错过了什么吗?是try_realloc不是没有我想象的那么好用?是否有一些隐藏的错误导致try_realloc无法使用?

更好的是,是否有一些记录较少的 API 可以执行try_realloc

注意:我显然在这里使用库/平台特定代码。我并不担心,因为try_realloc它本质上是一种优化。


更新: 根据 Steve Jessops 关于vector使用 realloc 是否更有效的评论,我写了一个概念证明来测试。模拟realloc-vector向量的增长模式,但可以选择重新分配。我在向量中运行了多达一百万个元素的程序。

作为比较vector,在增长到一百万个元素时必须分配 19 次。

结果,如果realloc-vector是唯一使用堆的东西,结果非常棒,3-4 分配,同时增长到百万字节的大小。

如果与以 66% 的速度增长的realloc-vectora 一起使用,则 结果不太乐观,在增长期间分配 8-10 次。vectorrealloc-vector

最后,如果与以相同速率增长的realloc-vectora 一起使用,则分配 17-18 次。几乎没有比标准向量行为节省一个分配。vectorrealloc-vector

我不怀疑黑客可以通过游戏分配大小来节省成本,但我同意史蒂夫的观点,即编写和维护这样一个分配器的巨大努力并没有带来收益。

4

3 回答 3

11

vector一般以较大的增量增长。你不能在不重新定位的情况下重复这样做,除非你仔细安排事情,以便在向量的内部缓冲区上方有大量空闲地址(这实际上需要分配整个页面,因为显然你不能有其他分配稍后在同一页面上)。

所以我认为,为了在这里获得真正好的优化,你需要的不仅仅是一个“微不足道的解决方法”,如果可能的话,它会进行廉价的重新分配——你必须以某种方式做一些准备以使其成为可能,而这种准备会花费你的地址空间. 如果你只为某些向量做这件事,那些表明它们会变大的向量,那么这是毫无意义的,因为它们可以表明reserve()它们会变大。如果您有一个巨大的地址空间,您只能为所有向量自动执行此操作,这样您就可以在每个向量上“浪费”一大块。

据我了解,这个Allocator概念没有重新分配功能的原因是为了保持简单。如果std::allocator有一个try_realloc函数,那么要么每个分配器都必须有一个(在大多数情况下无法实现,并且只需要始终返回 false),要么每个标准容器都必须专门用于std::allocator利用它。这两个选项都不是一个很好的 Allocator 接口,尽管我认为对于几乎所有 Allocator 类的实现者来说,仅仅添加一个无所事事的try_realloc功能不会是一项巨大的努力。

如果vector由于重新分配而变慢,deque可能是一个很好的替代品。

于 2010-11-03T16:38:32.237 回答
4

您可以使用with和with来实现类似于try_realloc您建议的内容。mmapMAP_ANONYMOUSMAP_FIXEDmremapMREMAP_FIXED

编辑:刚刚注意到 mremap 的手册页甚至说:

mremap() 使用 Linux 页表方案。mremap() 更改虚拟地址和内存页之间的映射。这可用于实现非常高效的 realloc(3)。

于 2010-11-03T16:29:53.670 回答
2

realloc在 C 中只是一个便利函数;它对性能/减少副本几乎没有好处。我能想到的主要例外是分配一个大数组的代码,然后在知道所需大小后减小大小 - 但即使这样也可能需要在某些malloc实现上移动数据(那些严格按大小分隔块的实现),所以我认为这种用法realloc真的很糟糕的做法。

只要您不是每次添加元素时都不断地重新分配数组,而是在空间不足时以指数方式增长数组(例如,增长 25%、50% 或 100%),只需手动分配新内存,复制和释放旧的将产生与 using 大致相同(并且在内存碎片的情况下相同)的性能realloc。这肯定是 C++ STL 实现使用的方法,所以我认为您的全部担忧是没有根据的。

编辑:一个(罕见但并非闻所未闻)realloc实际有用的情况是对于具有虚拟内存的系统上的巨型块,其中 C 库与内核交互以将整个页面重新定位到新地址。我说这很少见的原因是因为您需要处理非常大的块(至少几百 kB),然后大多数实现甚至会进入处理页面粒度分配的领域,并且可能更大(可能有几个 MB)在进入和退出内核空间之前重新排列虚拟内存比简单地复制要便宜。当然try_realloc在这里没有用处,因为所有的好处都来自于实际成本低廉的移动。

于 2010-11-03T18:39:19.043 回答