我正在使用XNA开发Xbox 360游戏。我真的喜欢在几个地方使用C#的yield return
构造,但它似乎造成了很多垃圾。看看这段代码:
class ComponentPool<T> where T : DrawableGameComponent
{
List<T> preallocatedComponents;
public IEnumerable<T> Components
{
get
{
foreach (T component in this.preallocatedComponents)
{
// Enabled often changes during iteration over Components
// for example, it's not uncommon for bullet components to get
// disabled during collision testing
// sorry I didn't make that clear originally
if (component.Enabled)
{
yield return component;
}
}
}
}
...
我使用这些组件池到处 - 用于子弹,敌人,爆炸;任何无数和短暂的。我经常需要遍历它们的内容,而我只对活跃的组件(即Enabled == true
)感兴趣,因此Components
属性的行为。
目前,在使用这种技术时,我每秒钟会看到大约800K的额外垃圾。这是可以避免的吗?还有其他方法可以使用yield return
吗?
编辑:我发现this question关于如何在不创建垃圾的情况下迭代资源池的更广泛问题。许多评论者都不屑一顾,显然不理解Compact Framework的局限性,但是this commenter更加同情并建议创建一个迭代器池。这是我将要使用的解决方案。
答案 0 :(得分:3)
只是为了咧嘴笑,尝试在Linq查询中捕获过滤器并保持查询实例。每次枚举查询时,这可能会减少内存重新分配。
如果不出意外的话,preallocatedComponents.Where(r => r.Enabled)
语句就会减少很少的代码来处理与收益率回报相同的事情。
class ComponentPool<T> where T : DrawableGameComponent
{
List<T> preallocatedComponents;
IEnumerable<T> enabledComponentsFilter;
public ComponentPool()
{
enabledComponentsFilter = this.preallocatedComponents.Where(r => r.Enabled);
}
public IEnumerable<T> Components
{
get { return enabledComponentsFilter; }
}
...
答案 1 :(得分:2)
编译器实现的迭代器确实使用了类对象,并且使用yield return
实现的迭代器的使用(例如foreach)确实会导致分配内存。在事物的方案中,这很少是一个问题,因为要么在迭代时完成相当多的工作,要么在迭代时分配更多的内存来做其他事情。
为了使迭代器分配的内存成为问题,您的应用程序必须是数据结构密集型的,并且您的算法必须在不分配任何内存的情况下对对象进行操作。想想类似的Game of Life。突然间,迭代本身就是压倒性的。当迭代分配内存时,可以分配大量的内存。
如果您的应用程序符合此配置文件(并且仅适用于),那么您应遵循的第一条规则是:
例如,如果您有一个数组或列表之类的数据结构,那么您已经公开了一个索引器属性和一个count属性,因此客户端可以简单地使用for循环而不是使用foreach和迭代器。这是减少GC的“轻松赚钱”,它不会使你的代码变得丑陋或臃肿,只是不那么优雅。
你应该遵循的第二个原则是: