C# 的“收益回报”为我制造了很多垃圾。可以帮忙吗?

发布于 2024-11-03 20:49:48 字数 1421 浏览 1 评论 0原文

我正在使用 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吗?

编辑:我发现这个问题关于如何迭代一个更广泛的问题资源池,不产生垃圾。许多评论者对此不屑一顾,显然不理解紧凑框架的局限性,但是 这位评论者更有同情心,并建议创建一个迭代器池。这就是我要使用的解决方案。

I'm developing an Xbox 360 game with XNA. I'd really like to use C#'s yield return construct in a couple of places, but it seems to create a lot of garbage. Have a look at this code:

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;
                    }
                }
            }
        }
    ...

I use these component pools everywhere - for bullets, enemies, explosions; anything numerous and transient. I often need to loop over their contents, and I'm only ever interested in components that are active (i.e., Enabled == true), hence the behavior of the Components property.

Currently, I'm seeing as much as ~800K per second of additional garbage when using this technique. Is this avoidable? Is there another way to use yield return?

Edit: I found this question about the broader issue of how to iterate over a resource pool without creating garbage. A lot of commenters were dismissive, apparently not understanding the limitations of the Compact Framework, but this commenter was more sympathetic and suggested creating an iterator pool. That's the solution I'm going to use.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

但可醉心 2024-11-10 20:49:48

编译器对迭代器的实现确实使用了类对象,并且使用(例如,使用 foreach)使用 yield return 实现的迭代器确实会导致分配内存。在事情的计划中,这很少是一个问题,因为要么在迭代时完成大量工作,要么在迭代时分配更多内存来做其他事情。

为了使迭代器分配的内存成为问题,您的应用程序必须是数据结构密集型的,并且您的算法必须在不分配任何内存的情况下对对象进行操作。想想类似的生命游戏。突然之间,迭代本身就变得不堪重负。当迭代分配内存时,可以分配大量内存。

如果您的应用程序符合此配置文件(并且仅当),那么您应该遵循的第一条规则是:

  • 当有更简单的迭代概念可用时,避免在内部循环中使用迭代器

例如,如果您有一个类似数组或列表的数据结构,那么您已经暴露了索引器属性和计数属性,因此客户端可以简单地使用 for 循环,而不是在迭代器中使用 foreach。这是减少 GC 的“轻松赚钱”,它不会使您的代码变得丑陋或臃肿,只是稍微不那么优雅。

您应该遵循的第二个原则是:

  • 测量内存分配以了解应在何时何地使用第一条规则

The implementation of iterators by the compiler does indeed use class objects and the use (with foreach, for example) of an iterator implemented with yield return will indeed cause memory to be allocated. In the scheme of things this is rarely a problem because either considerable work is done while iterating or considerably more memory is allocated doing other things while iterating.

In order for the memory allocated by an iterator to become a problem, your application must be data structure intensive and your algorithms must operate on objects without allocating any memory. Think of the Game of Life of something similar. Suddenly it is the iteration itself that overwhelms. And when the iteration allocates memory a tremendous amount of memory can be allocated.

If your application fits this profile (and only if) then the first rule you should follow is:

  • avoid iterators in inner loops when a simpler iteration concept is available

For example, if you have an array or list like data structure, you are already exposing an indexer property and a count property so clients can simply use a for loop instead of using foreach with your iterator. This is "easy money" to reduce GC and it doesn't make your code ugly or bloated, just a little less elegant.

The second principle you should follow is:

  • measure memory allocations to see when and where you should use with the first rule
月依秋水 2024-11-10 20:49:48

只是为了一笑,尝试捕获 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; }
        }
    ...

Just for grins, try capturing the filter in a Linq query and holding onto the query instance. This might reduce memory reallocations each time the query is enumerated.

If nothing else, the statement preallocatedComponents.Where(r => r.Enabled) is a heck of a lot less code to look at to do the same thing as your yield return.

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; }
        }
    ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文