最小化malloc()调用的数量可以提高性能?

考虑两个应用程序:一个(num.1)多次调用malloc(),另一个(num.2)调用malloc()几次。 两个应用程序分配相同数量的内存(假设为100MB)。
对于哪个应用程序,下一个malloc()调用会更快,#1还是#2?
换句话说:malloc()在内存中是否有分配位置的索引?

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

正如另一个答案所评论的那样,通常会有一个空闲块列表,但是如果你没有调用free,那么就只有一个,所以在两种情况下都应该是O(1)。

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

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

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

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

你问了2个问题:

  • 对于哪个应用程序,下一个malloc()调用会更快,#1还是#2?
  • 换句话说:malloc()在内存中是否有分配位置的索引?

你暗示他们是同一个问题,但他们不是。 后一个问题的答案是肯定的。

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

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

我不推荐这种方法; 它只是说明malloc的使用会对性能产生重大影响。

我的建议是衡量它

Malloc必须通过链接的空闲块列表来查找要分配的空闲块。 这需要时间。 所以,#1通常会变慢:

  • 您调用malloc的次数越多,所需的时间就越多 – 因此减少调用次数会使您的速度提高(尽管它是否显着取决于您的具体情况)。

  • 另外,如果你使用malloc许多小块,那么当你释放这些块时,你将比分配和释放一些大块更多地分割堆。 因此,您可能最终会在堆上放置许多小的空闲块而不是几个大块,因此您的malloc可能必须进一步搜索可用空间列表以找到合适的块进行分配。 再次使它们变慢。

这些当然是实现细节,但通常free()会将内存插入到空闲块列表中。 然后malloc()将查看此列表中是否有正确大小或更大的空闲块。 通常,只有当这个失败时, malloc()才会要求内核提供更多内存。

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

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

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

所有malloc()实现都有一个索引机制,但是向索引添加新块的速度通常不依赖于索引中已有的块数。

malloc的大部分缓慢来自两个来源

  • 在先前释放的(块)中搜索合适的空闲块
  • 多处理器锁定问题

编写我自己几乎符合标准的malloc()替换工具malloc()&& free()的时间从35%到3-4%,并且它严重优化了这两个因素。 它可能与使用其他高性能malloc的速度相似,但拥有我们自己的更便携的神秘设备,当然允许在某些地方免费内联。

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

实现自己的子分配器是否是个好主意是次要问题。 很少,显式内存管理已经足够困难了。 您很少需要另一层代码,如果没有任何好的调试方法,可能会破坏程序并使程序崩溃。 除非您正在编写调试分配器。

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

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

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

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

但是,在分配小块内存而不是一个大块时,可能会有更好的成功机会。 您的程序是分配一个小块并释放它还是需要分配(并保留)小块。 当内存碎片化时,可用的块数较少,因此内存分配器可能必须合并所有块以形成足够大的块以进行分配。

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