2010-01-16 67 views
27

考虑两个应用程序:多次调用malloc()的一个(num。1),以及调用malloc()几次的另一个(num。2)。 这两个应用程序都分配相同的内存量(假设为100MB)。
对于哪个应用程序,下一个malloc()调用会更快,#1或#2?
换句话说:malloc()是否有内存中分配位置的索引?最小化malloc()调用的数量可以提高性能?

+1

它确实(有一个分配位置的索引) - “free”如何工作,但不需要使下一个'malloc'调用更小。如果其中一个程序已经分配并释放了很多并创建了碎片,那么这会使下一个'malloc'调用更慢,因为空闲列表将是一长串块,其中大部分块太小。 – 2010-01-16 22:38:33

+0

一个观察结果是,一旦内存资源变得紧张,malloc处理较小的内存块可能是更好的方法。在这里或那里寻找一小块空闲的内存可能比较容易,而不是某处的巨块。不知道这将如何影响性能。 – 2010-01-16 22:38:49

+2

malloc/free数据结构通常保留空闲块的链表,并且通常不会跟踪分配的块。它通常在头部预先分配数据。在免费它看起来在标题中找到分配的大小,然后将其添加到空闲块的链接列表。所以有一个空闲块的列表(但不是索引),除了程序员本身,没有任何记录分配块。 (当然,一个malloc实现可以做到这一点,它可能是一个很好的调试内存泄漏的方法。) – benno 2010-01-16 23:33:29

回答

10

当然,这完全依赖于malloc的实现,但在这种情况下,如果没有调用free,大多数malloc实现可能会给你相同的算法速度。

作为另一个答案评论,通常会有一个空闲块列表,但如果你没有免费调用,只会有一个,所以它应该是O(1)在这两种情况下。

这假定在这两种情况下为堆分配的内存足够大。在#1的情况下,您将分配更多的总内存,因为每个分配涉及用于存储元数据的内存开销,因此您可能需要调用sbrk(),或者等价于在#1情况下增加堆,这会添加额外的开销。

由于缓存和其他二级效应,它们可能会有所不同,因为新分配的内存对齐方式不会相同。

如果你已经释放了一些内存块,那么由于较少的碎片以及更少的空闲块搜索列表,很可能#2会更快。

如果你已经释放了所有的内存块,它应该最终完全一样,因为任何理智的免费实现都会将块合并回单个内存区域。

3

这些当然是实现细节,但通常free()会将内存插入到空闲块列表中。然后,malloc()将查看此列表以查找空白大小合适或更大的空白块。通常,只有在这种情况下,malloc()才会向内核请求更多的内存。

还有其他考虑因素,例如何时将多个相邻块合并为一个较大的块。

而另一个原因malloc()是昂贵的:如果从多个线程调用malloc(),这些全局结构必须有某种同步。 (即,锁)。存在具有不同优化方案的malloc()实现,以使其更适合多线程,但通常,保持其多线程安全性会增加成本,因为多线程将争用这些锁并阻止彼此的进度。

2

答案是,它的大部分潜在缓慢来自malloc()和free()的组合,通常#1和#2将具有相似的速度。

所有malloc()实现都具有索引机制,但向索引添加新块的速度通常不取决于索引中已有块的数量。

大多数的malloc的缓慢的有两个来源

  • 搜索所述以前释放(块)之间的一个合适的空闲块
  • 多处理器问题锁定

写我拥有几乎符合标准的malloc()替换工具malloc()& & free()times 35%to 3-4%,并认真优化了这两个因素。使用其他一些高性能的malloc可能会有类似的速度,但是我们自己可以更容易地使用深奥的设备,当然也可以在某些地方自由地插入内存。

6

Malloc必须通过空闲块的链接列表才能找到要分配的块。这需要时间。因此,#1通常会比较慢:

  • 越频繁调用malloc,更多的时间将需要 - 所以降低电话会给你一个速度的提升的数量(尽管无论是显著将取决于根据你的具体情况)。另外,如果malloc中有很多小块,那么当你释放这些块时,你将会比如果只分配并释放几个大块更多地分割堆。所以你可能最终会在你的堆上有许多小的空闲块,而不是几个大块,因此你的malloc可能不得不通过空闲空间列表进一步搜索以找到合适的块分配。这又会让他们变慢。

+0

如果堆中有很多小对象,则堆堆碎片会导致性能下降。 – pjc50 2010-02-05 11:33:41

+0

关于第一个要点:和其他答案一样,如果你只调用malloc(并且没有空闲),那么时间在使用空闲块列表的实现中保持不变,这似乎是常见的情况 - 偶尔出现打嗝时堆需要增长。 – hmijail 2017-02-22 10:44:35

+0

我的观点是,调用任何函数100次会引起100次调用同一函数的开销。 – 2017-02-22 22:30:41

18

你问两个问题:

  • 依据申请下一个的malloc()调用会更快,1号或2?
  • 换句话说:malloc()是否有内存中分配位置的索引?

你已经暗示,他们是同样的问题,但事实并非如此。后一个问题的答案是YES。

至于哪个会更快,这是不可能的。它取决于分配器算法,机器状态,当前进程中的碎片等等。

但是,您的想法很有道理:您应该考虑malloc的使用情况如何影响性能。 曾经有一个我写的应用程序使用了很多小内存块,每个内存块都分配了malloc()。它工作正常,但速度很慢。我只用一个替换了malloc的很多调用,然后在我的应用程序中切分了大块。速度要快得多。

我不推荐这种方法;这只是一个例子,表明malloc的使用可以实质性地影响性能。

我的建议是测量它

+1

对不起带来一个旧帖子,但一个问题;为什么你不推荐这种方法? – Fingolfin 2012-11-08 12:26:36

+2

我不推荐它,一般来说。我建议保持简单。 YAGNI。如果你看到内存分配的性能问题,尽一切办法,尝试不同的方法,并*测量它们*。但自从我遇到这个问题以来,内存分配算法已经有了很大的改进。 – Cheeso 2012-11-11 06:50:12

1

您没有定义“many”和“few”之间的相对差异,但我怀疑大多数malloc在两种情况下的功能几乎相同。这个问题意味着每次调用malloc都会有系统调用和页表更新的开销。当你进行malloc调用时,例如malloc(14)在一个非大脑死亡的环境中,malloc实际上会分配比你想要的更多的内存,通常是系统MMU页面大小的倍数。你得到你的14个字节,并且malloc跟踪新分配的区域,以便稍后的调用可以返回一块已经分配的内存,直到需要从操作系统请求更多的内存。

换句话说,如果我一次调用malloc(14)100次或malloc(1400),开销将大致相同。我只需要自己管理更大的分配内存块。

2

你可以总是使用malloc()分配一大块内存并自行细分它会做得更好。 Malloc()经过优化,可以在一般情况下运行良好,并且不会假设您是否使用线程或者程序的分配大小是多少。

实现自己的子分配器是个好主意是次要问题。很少情况下,显式内存管理已经够用了。你很少需要另一层代码,可以搞砸你的程序,没有任何好的方法来调试它。除非你正在编写调试分配器。

1

分配一块内存比分配多块更快。有系统调用的开销,也搜索可用的块。在编程中减少操作的次数通常会加快执行时间。

内存分配器可能必须搜索以找到正确大小的内存块。这增加了执行时间的开销。

但是,在分配小块内存与一个大块时,可能会有更好的成功机会。你的程序是分配一个小块并释放它,还是需要分配(并保留)小块。当内存变得碎片时,可用的块很少,所以内存分配器可能不得不合并所有块以形成足够大的分配块。

如果您的程序正在分配和销毁很多小块内存,您可能需要考虑分配一个静态数组并将其用于您的内存。