【问题标题】:Understanding Memory Pools了解内存池
【发布时间】:2017-05-30 04:52:53
【问题描述】:

据我了解,内存池是一个块,或者是运行前在堆栈上分配的多个内存块。
相比之下,据我了解,动态内存是从操作系统请求的,然后在运行时分配到堆上。

// 编辑 //

  • 内存池显然不一定在堆栈上分配,即。内存池可以与动态内存一起使用。
  • 根据这个问题的答案,显然不一定在堆栈上分配非动态内存。
  • “动态与静态内存”“内存池”的主题虽然答案仍然相关,但实际上并不相关。 李>

据我所知,内存池的目的是提供对 RAM 的手动管理,其中内存必须由程序员跟踪和重用。

这在理论上有利于性能,原因有很多:

  1. 随着时间的推移动态内存变得碎片化
  2. CPU 解析静态内存块的速度比动态块快
  3. 当程序员可以控制内存时,他们可以根据具体的程序选择在最好的时候释放和重建数据。

4、多线程时,分离池允许不同线程独立运行,无需等待共享堆(Davislor)

我对内存池的理解正确吗?如果是这样,为什么似乎不经常使用内存池?

【问题讨论】:

  • 您可能需要单独的内存池的一个原因是,如果您有多个线程同时分配和释放内存。如果它们都使用同一个堆,则它们必须等待其他线程完成对堆的修改才能继续。如果每个线程都有自己的内存池,它可以使用它而无需任何锁定或等待。
  • “CPU 可以解析静态内存块...” - 这完全是不是 CPU 的工作方式。事实上,典型的 CPU 不知道程序中存在哪些类型的内存——他们甚至不知道程序是用什么语言编写的!
  • @davislor 这也是一个好点,我稍后会在问题中编辑它。
  • @msalters 也许我应该将“解析”一词更改为“访问”。我被告知的是 CPU 实际上可以更快地循环通过静态内存。也许是因为它被打包得更紧/更有可能对齐,因此缓存未命中率更低?
  • @davislor 问题已编辑,添加了关于线程的部分

标签: c++ performance optimization memory-management memory-pool


【解决方案1】:

这个问题似乎与XY problempremature optimisation 相冲突。

您应该专注于编写清晰代码,然后使用分析器执行优化如果需要

我对内存池的理解正确吗?

不完全是。

...在堆栈上...

...在堆上...

存储持续时间与池的概念是正交的; 可以将池分配为具有任何四种存储持续时间(它们是:静态、线程、自动和动态存储持续时间)。

C++ 标准不要求 任何 进入 stackheap;将它们全部视为进入同一个地方可能会很有用...毕竟,它们都(通常)进入硅芯片

...在运行前分配...

重要的是,多个对象的分配发生在(或至少不那么频繁)这些对象第一次被使用之前;这样就不必分别分配每个对象。我认为这就是您所说的“在 runtime 之前”。在选择分配大小时,越接近在任何给定时间所需的对象总数,过度分配造成的浪费就越少,过度调整大小造成的浪费也就越少。

但是,如果您的操作系统不是史前操作系统,那么池的优势将很快消失。如果您在在进行优化之前和之后使用分析器,您可能会看到这一点!

  1. 随着时间的推移,动态内存变得碎片化

这可能适用于 Windows 1.0 等幼稚操作系统。然而,在当今时代,具有分配存储持续时间的对象通常存储在虚拟内存中,它会定期写入磁盘并从磁盘读取(这称为分页) .因此,可以对碎片化的内存进行碎片整理,并且更常用的对象、函数和方法甚至可能最终被合并到常见的页面中。

分页为你形成一个隐含的(以及缓存预测)!

CPU 解析静态内存块的速度比动态块快

虽然分配有静态存储持续时间的对象通常位于堆栈上,但这并不是 C++ 标准的强制要求。完全有可能存在 C++ 实现,而 静态内存块 被分配在堆上

对动态对象的缓存命中与对静态对象的缓存命中一样快。恰好堆栈通常保存在缓存中;您应该尝试在没有堆栈的情况下进行编程一段时间,您可能会发现缓存有更多空间用于

在优化之前,您应该始终使用分析器来衡量最重要的瓶颈!然后您应该执行优化,然后再次运行分析器以确保优化成功!

这不是一个独立于机器的进程!您需要优化每个实现一个实现的优化可能是对另一个实现的悲观

如果是这样,为什么看起来内存池不经常使用?

上面描述的虚拟内存抽象,结合使用缓存分析器消除猜测实际上消除了的有用性,除了最不知情的(即使用分析器)场景。

【讨论】:

  • 提出了一些非常好的观点。已编辑问题,已接受答案。
【解决方案2】:

自定义分配器可以提高性能,因为默认分配器针对特定用例进行了优化,不经常分配大块内存。

但是,例如在模拟器或游戏中,您可能在一帧中发生很多事情,非常频繁地分配和释放内存。在这种情况下,默认分配器就没有那么好了。

一个简单的解决方案可以是为一帧期间发生的所有一次性内容分配一块内存。这块内存可以一遍又一遍地覆盖,删除可以推迟到以后。例如:游戏关卡结束或其他。

【讨论】:

    【解决方案3】:

    内存池用于实现自定义分配器。

    一种常用的是线性分配器。它只保留一个分隔已分配/空闲内存的指针。使用它进行分配只是将指针增加 N 个请求的字节,并返回它的先前值。并且释放是通过将指针重置为池的开头来完成的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-08
      • 2019-06-02
      • 2011-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多