【问题标题】:One large malloc versus multiple smaller reallocs一个大的 malloc 与多个较小的 realloc
【发布时间】:2010-10-24 12:07:34
【问题描述】:

很抱歉,如果之前有人问过这个问题,我无法找到我正在寻找的东西。

我正在从列表中读取字段并将它们写入内存块。我可以

  • 遍历整个列表,找到所需的总大小,做一个malloc,然后再次遍历列表并复制每个字段;
  • 在我写入值时遍历整个列表和realloc 内存块;

现在第一个对我来说似乎是最有效的(调用次数最少)。两种方法的优缺点是什么?

感谢您的宝贵时间。

【问题讨论】:

    标签: c memory-management malloc realloc


    【解决方案1】:

    第一种方法几乎总是更好。 realloc() 通常通过将内存块的全部内容复制到新分配的更大块中来工作。所以 n reallocs 可能意味着 n 个副本,每一个都比上一个大。 (如果每次都在分配中添加 m 个字节,那么第一个 realloc 必须复制 m 个字节,下一个 2m,下一个 3m,...)。

    迂腐的答案是 realloc() 的内部性能影响是特定于实现的,没有由标准明确定义,在某些实现中,它可以由立即移动字节的魔法仙女工作,等等等等 - 但在任何实际的实现,realloc() 都意味着一个副本。

    【讨论】:

    • “立即移动字节的魔法仙女” - “仙女”又名“VMM”。为每个分配至少分配一个页面,只是为了优化 realloc,这是荒谬的,但我想你可以在不需要大量魔法的情况下为大型分配做到这一点。我想你有时会在 I/O 堆栈中看到一个有点像它的技巧,其中缓冲区通过重新映射从内核“移交”到用户空间而无需复制。
    • 除非你的 malloc 代码是由智障人士编写的:-),如果你不能扩展当前块,你通常只会得到一个副本。如果您尝试重新分配的块位于竞技场的尽头,则通常情况并非如此。同样,您说得对,这是一个实现问题,但我从未见过不利用底层数据结构属性的实现。如果重新分配是你唯一要做的事情,你很可能不会得到很多复制操作。
    • 令人震惊的是,这意味着某大厂商的标准库提供的realloc()肯定是脑残写的。这是一个令人不安的想法。
    • 哪一个?拜托,让它成为微软吧​​,我确实喜欢偶尔抨击 MS :-) 说真的,对于特定的实现来说,可能有一些减轻原因,更喜欢“复制”而不是“合并”,所以这取决于具体情况。但是,如果在您尝试重新分配的块之后立即有一个足够大的空闲块,并且您的 arena 实现支持它,那么副本不太可能比两个块的简单合并更快。
    • 你也可以在Solaris上查看realloc的代码,你会看到它会复制块并且不使用vmm技巧,这与malloc等的大多数实现是合乎逻辑的。在用户空间中,vmm 技巧将意味着切换到内核模式。通常通过首先过度分配块来避免复制。对于小块,它分配 100% 以上,对于大块 50%。
    【解决方案2】:

    根据您认为最有可能的最大值,最初分配相当数量的空间可能会更好。

    然后,如果您发现需要更多空间,不要只为额外空间分配足够的空间,而是分配一大块额外空间。

    这将最大限度地减少重新分配的次数,同时仍然只处理一次列表。

    例如,最初分配 100K。如果你发现你需要更多,重新分配到 200K,即使你只需要 101K。

    【讨论】:

    • 我建议查看 Alexandrescu 的小对象分配器,以获得类似的想法和非常好的实现。
    • @pax:啊,“以指数方式重新分配的因素”论点。快乐的时光。我看过 2,我看过 3/2,我看过 577/408...
    • 我已经看到了每次加倍的建议,但我通常只是添加初始数量(因此总分配量为 100K、200K、300K,...)。这允许相当有效的扩展,但您永远不会浪费超过最初的 100K 数量。
    • @paxdiablo:如果您对最终大小一无所知,我会采用指数增长和最终realloc() 到正确大小;仅当您事先知道大多数输入只需要一些扩展时,线性增长才有意义
    • @Steve: 当然可以,但是如果不必要分配的内存量足够大,合理的realloc() 实现将(例如在Linux 上通过使用mremap());请参阅 blog.kowalczyk.info/article/realloc-on-Windows-vs-Linux-1.html 了解有关线性与指数增长性能的轶事证据
    【解决方案3】:

    不要重新发明轮子,而是使用 CCAN 的 darray,它实现了类似于 paxdiablo 描述的方法。见darray on GitHub

    【讨论】:

      猜你喜欢
      • 2012-07-23
      • 1970-01-01
      • 2012-11-29
      • 2014-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-27
      相关资源
      最近更新 更多