将简单的lambda表达式或本地函数分配给委托的性能

时间:2018-05-18 10:15:44

标签: c# lambda c#-7.0

当使用非常简单的表达式作为键来创建带Enumerable.ToLookup<TSource, TKey> Method (IEnumerable<TSource>, Func<TSource, TKey>)的ILookup时,我可以使用lambda表达式:

var lk = myItems.ToLookup((x) => x.Name);

或本地功能:

var lk = myItems.ToLookup(ByName);

string ByName(MyClass x)
{
     return x.Name;
}

我很好奇这个简单案例是否存在差异。

his answerLocal function vs Lambda C# 7.0 SO用户svick提供了一个很好的论据 - 为什么 - 通用 - 本地函数比lambdas更好。

重要的一点是绩效差异:

  

创建lambda时,必须创建一个委托,在这种情况下这是一个不必要的分配。本地函数实际上只是函数,不需要委托。

但是,由于我们将其传递给ToLookup(),无论如何都会创建一个委托。性能是否还有差异?
我可以想象编译器必须为myItems.ToLookup的每次调用创建一个新的委托lambda,而本地方法只需要一个委托实例;这是真的吗?

svick's answer中性能的第二个不同点是捕获变量和创建闭包:

  

此外,本地函数在捕获局部变量时更有效:lambdas通常将变量捕获到类中,而本地函数可以使用struct(使用ref传递),这又避免了分配。

但是,由于表达式不使用外部作用域中的变量,因此statedReed Copsey expanded不需要Eric Lippert作为闭包。回答Are Lambda expressions in C# closures?

  

lambda可以使用闭包实现,但它本身不一定是闭包。 - Reed Copsey
  [...]
  可以被视为对象的函数只是一个委托。使lambda成为闭包的原因是它捕获了它的外部变量。 - Eric Lippert

这有点矛盾Eric Lippert他自己是answerAssigning local functions to delegates Eric Lippert将本地函数解释为名为lambda:

  

本地函数基本上只是一个带有关联名称的lambda。

但是,这是一个较小的技术细节,对于的lambda / local函数的代理来捕获外部范围变量。

这个简单的表达式不是递归的,不是泛型的,也不是迭代器。看起来更好的是一个意见问题 那么,简单的非捕获,非递归,非泛型和非迭代器lambda表达式和本地函数之间的性能(或其他)是否有任何差异?

1 个答案:

答案 0 :(得分:5)

使用当前版本的编译器(Roslyn 2.8.0),带有lambda的版本效率更高,因为它会缓存委托。

关注the IL of code that has your two samples in separate methods,这是有效的:

sealed class HelperClass
{
    public static readonly HelperClass Instance = new HelperClass();

    public static Func<MyClass, string> CachedDelegate;

    internal string LambdaByName(MyClass x)
    {
        return x.Name;
    }

    internal string LocalFunctionByName(MyClass x)
    {
        return x.Name;
    }
}

void Lambda(IEnumerable<MyClass> myItems)
{
    var lk = myItems.ToLookup(HelperClass.CachedDelegate ??
        (HelperClass.CachedDelegate =
            new Func<MyClass, string>(HelperClass.Instance.LambdaByName)));
}

void LocalFunction(IEnumerable<MyClass> myItems)
{
    var lk = myItems.ToLookup(
        new Func<MyClass, string>(HelperClass.Instance.LocalFunctionByName)));
}

注意Lambda如何仅分配委托一次并在之后使用缓存委托,而LocalFunction每次都分配委托。这使Lambda在这种特定情况下更有效率。

虽然有a proposal on GitHub to change the compiler to make LocalFunction as efficient as Lambda