这是一个“难”的问题。我在网上找不到任何有趣的东西。
我正在为我的公司开发一个内存管理模块。我们为下一代游戏机开发游戏(Xbox 360,PS3和PC ......我们认为PC是控制台!)。
我们将来需要为下一款游戏处理无法在主控制台内存中加载的大型游戏世界的纹理流媒体(暂不讨论PC)。
我们将在纹理的开始高分辨率mipmap(即大约世界数据大小的70%)上进行流式传输。也许在未来我们还必须流式传输几何体,更小的mipmap,音频等。
我正在为这个问题开发一个内存管理器,专注于X360(因为在PS3上我们可以使用主机内存和相关的自动碎片整理GMM分配器)。
我面临的问题如下:我们决定为纹理流保留一个特定的内存区域(例如64兆字节),我们希望处理该区域中的所有分配和解除分配。我们已经在应用程序开始时分配了区域,并且该区域在物理上保证是连续的(不仅仅是虚拟,因为我们需要在那里存储纹理)。
我已经实现了一个自动碎片整理分配器,使用句柄而不是指针。时间不是问题,问题是内存碎片化。在游戏中我们不断加载和卸载流式目标,所以我们想使用最大量的缓冲区(64兆字节)。
使用这个分配器,我们可以使用所有分配的空间但是碎片整理程序在不可接受的时间内工作(有时是60毫秒,超过一帧!),而算法也不算太糟糕......太过于不可避免的memcpy!
我正在寻找解决此问题的解决方案。我想找到一篇好文章或验尸报告,或者遇到同样问题的人。
现在我选择两种策略: 1)在专用线程上移动碎片整理例程(适用于具有6个线程的X360线程,对于只有一个线程的PS3不好......并且不要告诉我使用SPU!)以及锁定区域的所有多线程问题,访问正在移动的地区,...... 2)找到碎片整理问题的“增量”解决方案:我们可以为每个帧提供时间预算(例如最多1毫秒)进行碎片整理,内存管理器将在每个帧的预算中执行它可以做的事情。
有人可以告诉我他的经历吗?
答案 0 :(得分:14)
我最近做了很多关于内存管理的研究,这是我在网上发现的最具信息量和帮助性的文章。
http://www.ibm.com/developerworks/linux/library/l-memory/
基于该论文,您将获得的最佳和最快的结果是将64 MB划分为相同大小的块。块的大小取决于您的对象大小。并一次分配或取消分配一个完整的块。这是
阅读它,你会发现有关每种可能解决方案的优秀信息,以及每种解决方案的优点和缺点。
答案 1 :(得分:5)
为什么不按纹理大小为流式纹理和池使用多个内存区域?
Insomniac有一篇关于PS3纹理流实现的论文。我想它可能会有所帮助:link。
对于最小化碎片的一般分配策略,也许Doug Lea可以提供帮助。
但是从我对你的问题的解读来看,这听起来像是在推翻它,我强烈推荐一种汇集方法。 (在写入组合内存上运行碎片整理传递听起来也不是特别安全或有趣。)
答案 2 :(得分:2)
由于您正在使用句柄,因此您可以自由地移动内存。我认为使用单独的线程可能不是最好的(最安全或最快)的方式 - 我的猜测是你最好使用一种增量复制分配器,每个malloc()
或{{1}你压缩(在内存中向前或向后复制)一些已分配的块,你复制的字节数耗尽“预算”,周期性地重置为其初始值(例如,在每次屏幕刷新时)。 (当然只复制整个块。)
这个想法是复制给定数量的字节需要相当可预测的时间,因此您可以估计每次屏幕刷新可以安全执行的复制字节数,并限制自己。如果预算中有足够的时间,则调用free()
或malloc()
将完全对内存进行碎片整理,否则会在给定的时间限制内尽可能对其进行碎片整理。
我在这里有一些问题未解决 - 例如究竟如何压缩内存。标准的非增量复制分配器可以从前面开始分配,然后在内存耗尽时将所有内容复制到后面(释放前面的内存),但是你没有这种自由。您可能需要一些启发式方法来决定是向前还是向后移动块。重要的是避免振荡(在free()
或malloc()
的连续调用中,同一块向前移动然后向后移动。
答案 3 :(得分:2)
我们几乎完全按照您描述的系统,除了我们分配固定大小的插槽 - 256x256,512x512,1024x1024和2048x2048纹理,每种格式有两种格式(DXT1和DXT5) - 正是为了避免内存管理。
答案 4 :(得分:1)
我建议采用增量方法。每个帧都找到一个连续的内存块,它在两侧都有自由空间,并在任何方向移动它以使其适合。或者你可以在一个方向上移动所有块,找到一个最适合它的间隙和一个使用块并移动它。在360上你应该使用一个线程来进行移动,而在PS3上,最好使用GPU为你移动数据。