为什么调用函数时会有内存分配 [英] Why are there memory allocations when calling a func

查看:27
本文介绍了为什么调用函数时会有内存分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下程序,它从两个静态方法构造一个本地 Func.但奇怪的是,当我分析程序时,它分配了接近一百万个 Func 对象.为什么调用 Func 对象也会创建 Func 实例?

I have the following program which construct a local Func from two static methods. But strangely, when I profile the program, it allocated close to a million Func objects. Why invoking Func object is also creating Func instances?

public static class Utils
{
    public static bool ComparerFunc(long thisTicks, long thatTicks)
    {
        return thisTicks < thatTicks;
    }
    public static int Foo(Guid[] guids, Func<long, long, bool> comparerFunc)
    {
        bool a = comparerFunc(1, 2);
        return 0;
    }
}
class Program
{
    static void Main(string[] args)
    {
        Func<Guid[], int> func = x => Utils.Foo(x, Utils.ComparerFunc);
        var guids = new Guid[10];
        for (int i = 0; i < 1000000; i++)
        {
            int a = func(guids);
        }
    }
}

推荐答案

你正在使用一个方法组转换来创建Func用于 comparerFunc 参数.不幸的是,C# 5 规范当前要求在每次运行时创建一个新的委托实例.来自 C# 5 规范第 6.6 节,描述了方法组转换的运行时评估:

You're using a method group conversion to create the Func<long, long, bool> used for the comparerFunc parameter. Unfortunately, the C# 5 specification currently requires that to create a new delegate instance each time it's run. From the C# 5 specification section 6.6, describing the run-time evaluation of a method group conversion:

分配了委托类型 D 的新实例.如果没有足够的可用内存来分配新实例,则会抛出 System.OutOfMemoryException 并且不会执行进一步的步骤.

A new instance of the delegate type D is allocated. If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.

匿名函数转换部分(6.5.1)包括:

The section for anonymous function conversions (6.5.1) includes this:

允许(但不要求)将具有相同(可能为空)捕获的外部变量实例集的语义相同的匿名函数转换为相同的委托类型,以返回相同的委托实例.

Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance.

...但是方法组转换没有类似的东西.

... but there's nothing similar for method group conversions.

这意味着此代码允许进行优化,以便为所涉及的每个委托使用单个委托实例 - Roslyn 确实如此.

That means this code is permitted to be optimized to use a single delegate instance for each of the delegates involved - and Roslyn does.

Func<Guid[], int> func = x => Utils.Foo(x, (a, b) => Utils.ComparerFunc(a, b));

另一种选择是分配一次 Func 并将其存储在局部变量中.该局部变量需要被 lambda 表达式捕获,这会阻止 Func<Guid[], int> 被缓存 - 这意味着如果您多次执行 Main,您将在每次调用时创建两个新的委托,而早期的解决方案将尽可能地缓存.代码更简单:

Another option would be to allocate the Func<long, long, bool> once and store it in a local variable. That local variable would need to be captured by the lambda expression, which prevents the Func<Guid[], int> from being cached - meaning that if you executed Main many times, you'd create two new delegates on each call, whereas the earlier solution would cache as far as is reasonable. The code is simpler though:

Func<long, long, bool> comparer = Utils.ComparerFunc;
Func<Guid[], int> func = x => Utils.Foo(x, comparer);
var guids = new Guid[10];
for (int i = 0; i < 1000000; i++)
{
    int a = func(guids);
}

<小时>

所有这一切都让我感到难过,在最新版本的 ECMA C# 标准中,编译器将被允许缓存方法组转换的结果.我不知道它何时/是否会这样做.


All of this makes me sad, and in the latest edition of the ECMA C# standard, the compiler will be permitted to cache the result of method group conversions. I don't know when/whether it will do so though.

这篇关于为什么调用函数时会有内存分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆