使用委托会产生垃圾吗

发布于 2024-08-08 00:17:12 字数 543 浏览 6 评论 0原文

我正在使用 XNA 为 xbox360 开发一款游戏。与 PC 上的垃圾收集器相比,Xbox 上的垃圾收集器的性能相当糟糕,因此将垃圾生成量降至最低对于顺利运行游戏至关重要。

我记得曾经读过,调用委托会产生垃圾,但现在我找不到任何对委托创建垃圾的引用。这是我编造的还是代表们搞砸了?

如果代表混乱,建议解决方法可加分。

public delegate T GetValue<T>(T value, T[] args);

public static T Transaction<T>(GetValue<T> calculate, ref T value, params T[] args) where T : class
{
    T newValue = calculate(value, args);
    return foo(newValue);
}

我的代码目前看起来有点像这样,我能想到的摆脱委托的唯一解决方案是传入一个继承接口 IValueCalculator 的类,然后我可以调用该接口上的方法,这并不是很简洁尽管!

I'm working on a game for the xbox360, using XNA. On the Xbox the garbage collector performs rather badly compared to the one on a PC, so keeping garbage generated to a minimum is vital for a smoothly performing game.

I remember reading once that calling a delegate creates garbage, but now for the life of me can't find any references to delegates creating garbage. Did I just make this up or are delegates messy?

If delegates are messy, bonus points for suggesting a workaround.

public delegate T GetValue<T>(T value, T[] args);

public static T Transaction<T>(GetValue<T> calculate, ref T value, params T[] args) where T : class
{
    T newValue = calculate(value, args);
    return foo(newValue);
}

My code looks vaguely like that at the moment, the only solution I can think of to rid myself of delegates is to pass in a class which inherits an interface IValueCalculator, and then I can call the method on that interface, that's not really very neat though!

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

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

发布评论

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

评论(4

亢潮 2024-08-15 00:17:12

在桌面环境中垃圾实际上是免费的。您需要担心的是您产生了多少非垃圾。请记住垃圾收集器的工作原理:它首先标记所有已知对象,然后清除所有活动对象上的标记并压缩活动对象。昂贵的步骤是“取消标记活动对象”。销毁垃圾成本低廉;识别活动对象的成本很高,而该成本取决于您拥有的活动对象的数量(及其参考拓扑的复杂性),而不是取决于您拥有的死亡对象的数量。

然而,在 XBOX 和其他紧凑框架上,垃圾收集器运行得相当频繁,并且在创建新分配时运行得更频繁,所以是的,您担心创建垃圾也是正确的。您希望既保持活动集较小(以便使集合便宜),又不进行新的分配(因为这会触发集合)。

创建委托确实会分配内存,但调用< /em> 其一只不过是在类上调用名为 Invoke 的方法。委托只不过是一个具有名为 Invoke 的方法的类,该方法在被调用时会立即调用另一个方法。

无论如何,如果您遇到内存性能问题,那么正确的做法是拿出内存分析器并使用它来分析您的程序。随机地想知道这个或那个是否碰巧分配了内存,就像试图用指甲剪给你的花园除草一样;这需要花费很多时间,而且并不能真正实现你的目标。使用分析器分析您的性能并查看问题所在,然后修复它们。

In the desktop environment garbage is effectively free. There what you want to worry about is how much non-garbage you are producing. Remember how the garbage collector works: it first marks all known objects, then it clears the mark on all live objects and compacts the live objects. The expensive step there is "unmark the live objects". Destroying the garbage is cheap; it's identifying the live objects that is expensive, and that cost depends on the number of live objects you have (and the complexity of their reference topology), not on the number of dead objects you have.

However, on XBOX and other compact frameworks, the garbage collector runs quite frequently and runs more often when new allocations are created, so yes, you are correct to worry about creating garbage too. You want to both keep the live set small (so as to make a collection cheap) and not make new allocations (because that triggers collections.)

Creating a delegate does allocate memory, but calling one is nothing more than calling a method named Invoke on a class. A delegate is not much more than a class with a method named Invoke that happens to immediately call another method when it is called.

Regardless, if you have a problem with memory performance then the right thing to do is to get out the memory profiler and use it to analyze your program. Casting about at random wondering if this or that happens to allocate memory is like trying to weed your garden with nail scissors; it takes a lot of time and doesn't actually accomplish your goals. Use a profiler to analyze your performance and see where the problems are, and then fix them.

倾听心声的旋律 2024-08-15 00:17:12

委托本身就是一个对象,因此如果您创建一个委托(也许是为匿名方法),并将其交给其他某个方法来执行,并且不存储该委托以供将来参考,那么是的,那个 会产生垃圾。

例如,

collection.ForEach(delegate(T item)
{
    // do something with item
});

在这种情况下,创建了一个新的委托对象,但除了对 ForEach 的调用之外,它没有被引用,因此符合垃圾回收的条件。

但是,调用委托本身不会产生垃圾,比调用任何其他相同类型的方法更会产生垃圾。例如,如果您调用采用 Object 参数的委托,并传入 Int32 值,则该值将被装箱,但如果您调用普通方法,则会发生这种情况同样的方式。

所以使用委托应该没问题,但是过多创建委托对象就会出现问题。


编辑:一篇关于 Xbox 和 XNA 内存管理的好文章位于:Xbox 360 上 XNA 的托管代码性能:第 2 部分 - GC 和工具 。请注意这句话:

那么如何控制 GC 延迟呢?与设备的 NetCF 一样,Xbox GC 是无世代的。这意味着每个集合都是托管堆上的完整集合。因此,我们发现 GC 延迟与活动对象的数量近似线性……然后添加堆压缩的成本。我们的基准测试表明,深层对象层次结构与浅层对象层次结构之间的差异可以忽略不计,因此主要是对象的数量。处理小物体往往比处理大物体更便宜。

正如您所看到的,尽量避免创建大量不必要的对象,您应该会做得更好。

A delegate is itself an object, so if you create a delegate, perhaps for an anonymous method, and give this to some other method to execute, and don't store the delegate for future reference, then yes, that will produce garbage.

For instance, this:

collection.ForEach(delegate(T item)
{
    // do something with item
});

In this case, a new delegate object is created, but beyond the call to ForEach it is not referenced, and thus eligible for garbage collection.

However, calling delegates does by itself not produce garbage, any more so than calling any other method of the same type would. For instance, if you call a delegate that takes an Object parameter, passing in an Int32 value, this value will be boxed, but that would happen if you called a normal method the same way as well.

So using delegates should be fine, but excessive creation of delegate objects will be a problem.


Edit: A good article on memory management for Xbox and XNA is here: Managed Code Performance on Xbox 360 for XNA: Part 2 - GC and Tools. Pay attention to this quote:

So how does one control GC latency? Like NetCF for devices, the Xbox GC is non-generational. That means every collection is a full collection on the managed heap. Thus, we find that GC latency is approximately linear to the number of live objects… then add the cost of heap compaction on to that. Our benchmarks show that the difference between deep object hierarchies vs. shallow ones is negligible, so it’s mostly the number of objects that matter. Small objects also tend to be a somewhat cheaper to deal with than big objects.

As you can see, try to avoid creating lots of unnecessary objects, and you should fare better.

感受沵的脚步 2024-08-15 00:17:12

正如其他人已经指出的那样,委托创建会产生垃圾。

在您的示例中,使用 params 参数也可能会生成垃圾。

考虑提供重载而不使用 params 关键字。

这就是为什么库方法中通常存在具有不同数量参数的重载以及使用 params 关键字的重载:

请参阅String.Format 方法 (String, Object[])

Format Method (String, Object)
Format Method (String, Object[])
...
Format Method (String, Object, Object)
Format Method (String, Object, Object, Object)

Delegate creation generates garbage, as others already noted.

In your example, using params argument probably generates garbage as well.

Consider providing overloads without using params keyword.

That is the reason why overloads with different number of arguments usually exist in library methods along with the one using params keyword:

See String.Format Method (String, Object[])

Format Method (String, Object)
Format Method (String, Object[])
...
Format Method (String, Object, Object)
Format Method (String, Object, Object, Object)
看春风乍起 2024-08-15 00:17:12

是和不是。

调用简单委托不会在堆上分配任何东西,
但创建委托在堆上分配 64 字节。

为了避免GC,您可以预先创建委托。

让我们验证一下:

using BenchmarkDotNet.Running;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<BenchmarkDelegate>();            
        }
    }
}

基准测试:

using BenchmarkDotNet.Attributes;

namespace Test
{
    [MemoryDiagnoser]
    public class BenchmarkDelegate
    {
        public delegate int GetInteger();

        GetInteger _delegateInstance;

        public BenchmarkDelegate()
        {
            _delegateInstance = WithoutDelegate;
        }

        [Benchmark]
        public int WithInstance() => RunDelegated(_delegateInstance);

        [Benchmark]
        public int WithDelegate() => RunDelegated(WithoutDelegate);

        public int RunDelegated(GetInteger del) => del();

        [Benchmark]
        public int WithoutDelegate() => 0;
    }
}

输出如下,向右滚动可看到分配的内存/操作列:

DefaultJob : .NET Core 2.2.1 (CoreCLR 4.6.27207.03, CoreFX 4.6.27207.03), 64bit RyuJIT
|          Method |       Mean |     Error |    StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|---------------- |-----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
|    WithInstance |  7.5503 ns | 0.0751 ns | 0.0702 ns |           - |           - |           - |                   - |
|    WithDelegate | 35.4866 ns | 1.0094 ns | 1.2766 ns |      0.0203 |           - |           - |                64 B |
| WithoutDelegate |  0.0000 ns | 0.0000 ns | 0.0000 ns |           - |           - |

       - |                   - |

Yes and no.

Calling simple delegate does not allocate any stuff on heap,
but creating a delegate allocate 64 bytes on heap.

In order to avoid GC, you can precreate the delegate.

Let's verify it:

using BenchmarkDotNet.Running;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<BenchmarkDelegate>();            
        }
    }
}

Benchmark:

using BenchmarkDotNet.Attributes;

namespace Test
{
    [MemoryDiagnoser]
    public class BenchmarkDelegate
    {
        public delegate int GetInteger();

        GetInteger _delegateInstance;

        public BenchmarkDelegate()
        {
            _delegateInstance = WithoutDelegate;
        }

        [Benchmark]
        public int WithInstance() => RunDelegated(_delegateInstance);

        [Benchmark]
        public int WithDelegate() => RunDelegated(WithoutDelegate);

        public int RunDelegated(GetInteger del) => del();

        [Benchmark]
        public int WithoutDelegate() => 0;
    }
}

The output following, scroll right to see Allocated Memory/Op column:

DefaultJob : .NET Core 2.2.1 (CoreCLR 4.6.27207.03, CoreFX 4.6.27207.03), 64bit RyuJIT
|          Method |       Mean |     Error |    StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|---------------- |-----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
|    WithInstance |  7.5503 ns | 0.0751 ns | 0.0702 ns |           - |           - |           - |                   - |
|    WithDelegate | 35.4866 ns | 1.0094 ns | 1.2766 ns |      0.0203 |           - |           - |                64 B |
| WithoutDelegate |  0.0000 ns | 0.0000 ns | 0.0000 ns |           - |           - |

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