为动态数据结构预分配内存

Posted

技术标签:

【中文标题】为动态数据结构预分配内存【英文标题】:Preallocate memory for dynamic data structure 【发布时间】:2016-06-28 16:56:16 【问题描述】:

我有一个问题/好奇。 假设我想实现一个列表,例如我基本上可以使用 cormen book 方法。其中解释了如何实现、插入、删除、键搜索等。

但是,关于内存使用的问题没有说明。例如,如果我想在整数列表中插入一个整数。例如,我可以首先创建一个节点(我在那里分配内存)插入整数,然后将节点插入列表中。如果我想删除一个整数,一旦我知道存储在哪个节点中,我必须释放内存。

我现在想知道是否可以更方便地预先分配内存来存储例如 10 个节点并保留指向要使用的空闲节点的指针。如果内存池已满,那么我会为 20 个节点重新分配内存,如果池很大,则我是此类池大小的一半(依此类推)。管理池当然更复杂,因为我需要例如处理可能的内存碎片等。

我说的有道理吗?还是没有意义?我在一本书中读到,关于游戏编程,内存预分配可以提高性能,但我想知道如何。

【问题讨论】:

这不如根据需要分配/释放内存方便,但效率要高得多。是的,这是有道理的。在某个点之后(当算法都固定时),几乎所有的优化都归结为内存管理(不仅仅是它来自哪里,还有它是如何使用的,例如缓存感知优化)。 如果您有很多分配,内存池是一件好事。如果只有少数几个,这只是开销。此外,任何当代的内存分配子系统都使用了某种池化。 一种常用的优化技术不是使用列表而是使用数组。列表不是有效的数据结构。也可以使用节点数组而不是列表。另见 B 树。 @ThomasMatthews,我并没有试图指出一个列表是否有效。我试图指出/理解在上下文中,如果连续分配/释放内存效率不高,则可以执行一堆插入/删除操作(这些操作对于树和图形也很常见)。 使用节点数组。链表可以使用数组索引而不是指向下一个节点的指针来实现。以这种方式使用数组意味着您不需要继续执行内存分配。 【参考方案1】:

这是一个既简单又复杂的问题。如果你在标准问题中操作,你真的不需要担心内存分配。例如,为 10 个节点预分配内存在任何规模上都不会有效,您的性能问题可能在其他地方。但是,如果您的程序每秒不断地分配和释放数百或数千个小对象,则可能会导致内存碎片,您可能需要编写自定义分配器。

除了std::vector::reserve 函数外,几乎没有标准容器没有任何方法来预分配元素存储。但是,所有这些都允许在构造函数中使用自定义分配器。此外,还有placement new 运算符。

你可以尝试用这些东西做实验,它们写起来很有趣,只是如果你绝对没有必要就不要在生产中使用它们。

【讨论】:

完全错误。 如果你在标准问题中操作,你真的不需要担心内存分配 - 在 mt 环境中,每次分配都是一场灾难。 @SergeyA 什么是“mt-environment”?【参考方案2】:

我现在想知道是否可以更方便地预先分配内存来存储例如 10 个节点并保留一个指向要使用的空闲节点的指针。

您基本上是在描述池分配器通常做什么(我假设您在谈论恒定大小的节点)。因此,对您的问题的简短回答是:是的,您可以通过使用带有列表容器的池分配器来提高性能。

普通编译器附带的内存分配器非常适合通用分配(即随机大小对象的分配)。但是,当您需要分配恒定大小的对象时,您应该考虑使用自定义池分配器。您可以很容易地理解为什么恒定大小的对象分配器比标准分配器执行得更快。

您可以编写自己的池分配器,但这不是一件容易的事,您最好考虑使用现有的分配器,例如 boost pool_allocator 或 fast_pool_allocator。

【讨论】:

以上是关于为动态数据结构预分配内存的主要内容,如果未能解决你的问题,请参考以下文章

sds(简单动态字符串) 内存预分配优化策略

CArray 和内存预分配

c语言 动态内存分配

C语言动态数据结构

58 动态内存分配

为啥我们不能在堆栈上分配动态内存?