【问题标题】:Perfomance-consumption of realloc() [closed]realloc() 的性能消耗
【发布时间】:2017-01-19 19:36:03
【问题描述】:

我想知道 realloc() 真正需要多少性能:我经常这样做是为了将可用内存区域扩展一个元素(=特定结构)。是 - 感谢 MMU - 这样的 realloc() 只是保留内存区域的扩展,还是在某些条件下可以想象的所有数据的完整复制?

据我所知,当 std::vector 的大小增加并且预定义的内存量太小时,它经常需要复制内存区域...

【问题讨论】:

  • @Olaf 请仔细阅读我的帖子:我不扩展结构,但我通过某种结构的元素扩展内存区域。然而这一切都不是我的问题......
  • “我经常这样做是为了将可用内存区域扩展一个元素(=特定结构)” - 读起来就像你的代码包括疯狂的转换(可以容易导致UB)。

标签: c++ c performance malloc realloc


【解决方案1】:

realloc 复制所有数据。假设其他任何事情都只是要求性能问题。 realloc可以避免复制的情况很少,你绝对不能指望它们。我见过不止一种realloc 的实现,它甚至不费心去实现代码以避免复制,因为这不值得。

MMU 与此无关,因为重新映射支持分配的内存页面的成本只有在您点击超过两个页面时才会得到回报。这是基于我 15 年前阅读的研究,从那时起内存复制变得更快,而内存管理由于 MP 系统而变得更加昂贵。这也仅适用于内核内部的零拷贝方案,没有传递系统调用开销,这很重要并且会减慢这里的速度。它还要求您的分配完全对齐和大小,进一步降低了以这种方式实现realloc 的有用性。

如果没有分配要扩展的内存块,realloc 最多可以避免复制数据。如果realloc 是您的应用程序唯一做的事情,您可能会很幸运,但只要有一点碎片或其他分配的东西,您就不走运了。始终假设 realloc 是 malloc(new_size); memcpy(new, old, old_size); free(old);

使用realloc 处理数组大小调整时的一个好习惯是跟踪数组中有多少元素并具有单独的容量。仅当元素数量达到容量时才增加容量和realloc。在每个 realloc 上将容量增加 1.5 倍(大多数人做 2 倍,这在文献中经常被推荐,但研究表明 2 倍会导致非常糟糕的内存碎片问题,而 1.5 倍几乎同样有效并且对内存更好)。像这样的:

if (a->sz == a->cap) {
    size_t ncap = a->cap ? a->cap + a->cap / 2 : INITIAL_CAP;
    void *n = realloc(a->a, ncap * sizeof(*a->a)); 
    if (n == NULL)
         deal_with_the_error();
    a->a = n;
    a->cap = ncap;
}
a->a[a->sz++] = new_element;

如果包含数组的结构为零初始化,这甚至适用于初始分配。

【讨论】:

    【解决方案2】:

    复制数据并不昂贵(尽管有些人可能不同意)。使用嵌入的 mallocfree 代价高昂,并且可能会占用几乎所有的执行时间,具体取决于您在做什么。 如果是这样,修复它应该会给你一个加速。

    This 是我告诉我事情花费了多少时间的方式。

    最简单的解决方案是减少执行次数。当您分配一个数组时,将其分配得特别大,然后跟踪自己实际使用了多少。

    【讨论】:

    • "复制数据并不是昂贵的部分。"你确定吗?如果我们在每个元素上复制数据,则复制数据的成本将是 O(n^2),而 malloc 的成本将介于 O(n) 和 O(n log n) 之间,合理的 @ 987654323@ 实施。
    • @Art:务实。拿几个堆栈样本,看看它在做什么。我正在传达我的经验,内存分配和释放很容易成为程序中的主要活动。 Big-O 就目前而言是可以的,但它忽略了常数因素,因此它不如实际软件中的采样有用。
    • memcpy() 确实不是很贵,我的应用程序中有一个错误导致过多的 memcpy() 但这并没有导致大量且耗时的 CPU 负载,我发现这个问题是因为相关线程并没有导致 CPU 上的预期负载(与真正进行一些计算的操作相比)
    • 我很务实。我在 90 年代阅读了有关零拷贝方案的论文,当时它风靡一时(直到 10 年前,由于零拷贝方案非常不切实际,它逐渐消失了),因为内存拷贝是一件非常昂贵的事情。我还花了很多时间在内存分配方面工作,并且知道mallocfree 在今天非常便宜,而复制内存仍然非常缓慢。我查看的所有其他个人资料顶部都有memcpy
    • @Art:我们可以争论 memcpymalloc/free 是否更便宜,这可能取决于我们的具体经验,但我认为这是较小的点。我已经看到 50% 到 90% 的时间都花在了这些事情上,而且在每种情况下,简单地将其称为 很多 并不是很难,从而节省了大部分时间。
    【解决方案3】:

    行为实际上取决于实现。但所有人都试图最大限度地降低重定位内存的成本。因为重定位对于性能来说是非常昂贵的。它对缓存有直接影响。我没有号码,但这是非常昂贵的操作。
    例如,在重定位的情况下,如果运行时面临重定位内存或扩展当前保留的两个选项,则选择后者。
    但这并不像我说的那么简单。它还必须考虑内存碎片。
    所以有几个权衡需要满足。
    对于您提到的vector,他们使用不同的方案。如果vector 保留了m 字节,并且它需要额外的n 字节,则运行时将分配2 * (n+m) 以最大限度地减少未来重定位的可能性。如果超过了新的尺寸,下次它会使用4 的系数而不是2;等等。我提到的数字不是真实的。
    我不是很了解实现,希望其他人能给你更具体的信息。

    【讨论】:

      猜你喜欢
      • 2014-07-18
      • 1970-01-01
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 2017-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多