我正在开发一个Xbox 360游戏与XNA。我真的很想在几个地方使用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
属性的行为。
目前,在使用这种技术时,我看到了每秒高达800 K的额外垃圾。这可以避免吗?还有其他方式使用yield return
吗?
编辑:我发现了这个问题关于如何在资源池上迭代而不创建垃圾这一更广泛的问题。许多评论者对此不屑一顾,显然不理解Compact的局限性,但这个评论员更有同情心,建议创建迭代器池。这就是我要用的解决方案。
发布于 2011-04-29 18:11:07
编译器实现迭代器确实使用类对象,使用使用yield return
实现的迭代器(例如使用foreach )确实会导致分配内存。在事物的方案中,这很少是一个问题,因为要么是在迭代时完成大量的工作,要么是分配了相当多的内存在迭代时执行其他事情。
为了使迭代器分配的内存成为一个问题,您的应用程序必须是数据结构密集型的,并且您的算法必须在对象上操作而不分配任何内存。想想类似的生命游戏。突然之间,迭代本身压倒了一切。当迭代分配内存时,可以分配大量的内存。
如果您的应用程序符合此配置文件(并且只有当),那么您应该遵循的第一条规则是:
例如,如果您有类似于数据结构的数组或列表,那么您已经公开了一个indexer属性和一个count属性,这样客户端就可以简单地使用For循环,而不是在您的迭代器中使用foreach。这是减少GC的“容易的钱”,它不会使您的代码丑陋或臃肿,只是稍微不那么优雅。
你应该遵循的第二个原则是:
发布于 2011-04-29 16:39:39
对于grins,尝试在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; }
}
...
https://stackoverflow.com/questions/5838177
复制相似问题