21

这是一个“难”的问题。我在网上没有发现任何有趣的东西。

我正在为我的公司开发一个内存管理模块。我们为下一代游戏机(Xbox 360、PS3 和 PC……我们将 PC 视为游戏机!)开发游戏。

未来,对于我们的下一款游戏,我们需要处理无法全部加载到主机内存中的大型游戏世界的纹理流传输(暂时不讨论 PC)。

我们将在开始时流式传输纹理的高分辨率 mipmap(大约是世界数据大小的 70%)。也许将来我们还必须流式传输几何、较小的 mipmap、音频等。

我正在为这个问题开发一个内存管理器,专注于 X360(因为在 PS3 上我们可以使用主机内存和相关的自动碎片整理 GMM 分配器)。

我面临的问题如下:我们决定为纹理流式传输保留一个特定的内存区域(例如 64 兆字节),并且我们希望处理该区域中的所有分配和解除分配。我们在应用程序开始时分配了该区域,并且该区域在物理上保证是连续的(不仅仅是虚拟的,因为我们需要在那里存储纹理)。

我实现了一个自动碎片整理分配器,使用句柄而不是指针。时间不是问题,问题是内存碎片。在游戏中,我们不断地加载和卸载流目标,因此我们希望使用我们的缓冲区的最大数量(64 兆字节)。

有了这个分配器,我们可以使用所有分配的空间,但是碎片整理程序在一个不可接受的时间内工作(有时 60 毫秒,超过一帧!)而算法还不错......有太多不可避免的 memcpy!

我正在寻找解决此问题的解决方案。我想至少找到一篇好论文,或验尸报告,或遇到与我相同问题的人。

现在我在两种策略之间进行选择:1)在专用线程上移动碎片整理例程(对于具有 6 个硬件线程的 X360 来说很好,对于只有一个硬件线程的 PS3 来说很糟糕......而且不要告诉我使用 SPU!)锁定区域的所有多线程问题,访问正在移动的区域,... 2)找到碎片整理问题的“增量”解决方案:我们可以给每个帧一个时间预算(例如最多 1 毫秒)进行碎片整理并且内存管理器将在每一帧的预算中做它可以做的事情。

有人能告诉我他的经历吗?

4

5 回答 5

13

我最近做了很多关于内存管理的研究,这是我在网上找到的最有用和最有用的文章。

http://www.ibm.com/developerworks/linux/library/l-memory/

根据那篇论文,您将获得的最佳和最快结果是将 64 MB 分成大小相等的块。块的大小将取决于您的对象大小。并一次分配或释放一个完整的块。它是

  1. 比增量垃圾收集更快。
  2. 更简单。
  3. 并在一定程度上解决了“过多的碎片化”问题。

阅读它,您会发现关于每一种可能的解决方案以及每种解决方案的优缺点的优秀信息。

于 2009-03-19T12:14:12.227 回答
5

为什么不按纹理大小为流纹理和池使用多个内存区域?

Insomniac 有一篇关于他们在 PS3 上实现纹理流的论文。我想它可能会有所帮助:链接

对于尽量减少碎片的一般分配策略,Doug Lea可能会有所帮助。

但是从我对您问题的阅读来看,听起来您想多了,我强烈建议您采用汇总方法。(此外,在写入组合内存上运行碎片整理过程听起来也不是特别安全或有趣。)

于 2009-03-20T19:47:02.230 回答
2

由于您使用的是句柄,因此您可以自由地移动内存。我认为使用单独的线程可能不是最好的(最安全或最快的)方式——我猜你最好使用一种增量复制分配器,在每个malloc()free()你压缩的地方(在内存中向前或向后复制) 一些已分配块的数量,您复制的字节数会耗尽定期重置为其初始值的“预算”(例如,在每次屏幕刷新时)。(当然只复制整个块。)

这个想法是复制给定数量的字节需要相当可预测的时间,因此您可以估计每次屏幕刷新可以安全地执行多少字节的复制,并将自己限制在这个范围内。如果预算有足够的时间,调用malloc()free()将完全整理内存,否则将在给定的时间限制内尽可能多地整理碎片。

这里有一些问题没有解决——例如,究竟如何压缩内存。标准的非增量复制分配器可以从前面开始分配,然后在内存用完时将所有内容复制到后面(释放前面的内存),但是您在这里没有这种自由。您可能需要一些启发式方法来决定是向前还是向后移动块。malloc()重要的是要避免振荡(相同的块在连续调用or时向前移动然后向后移动free())。

于 2009-03-19T17:02:01.340 回答
2

我们几乎拥有您描述的系统,除了我们在固定大小的插槽中分配 - 256x256、512x512、1024x1024 和 2048x2048 纹理,每种格式有两种(DXT1 和 DXT5) - 正是为了避免内存管理。

于 2009-07-12T14:13:09.710 回答
1

我会推荐一种增量方法。每个帧都找到一个连续的正在使用的内存块,该块两侧都有空闲空间,并将其移动到任何可以容纳的方向。或者您可以将所有块向一个方向移动,找到一个间隙和一个最适合它的正在使用的块并移动它。在 360 上,您可能应该使用线程来进行移动,而在 PS3 上,最好使用 GPU 来为您移动数据。

于 2009-03-20T19:28:20.217 回答