C#Lambda性能问题/可能性/指南

Rob*_*nik 28 c# lambda performance-testing lifting

我正在使用各种lambda表达式语法测试性能差异.如果我有一个简单的方法:

public IEnumerable<Item> GetItems(int point)
{
    return this.items.Where(i => i.IsApplicableFor(point));
}
Run Code Online (Sandbox Code Playgroud)

然后这里有一些与point参数相关的变量提升,因为它是lambda视角的自由变量.如果我将这种方法称为百万次,那么保持原样或以任何方式改变它以改善其性能会更好吗?

我有哪些选择,哪些选项实际可行?据我所知,我必须摆脱自由变量,因此编译器不必创建闭包类并在每次调用此方法时实例化它.与非闭合版本相比,此实例化通常需要大量时间.

问题是我想提出一些通常会起作用的lambda写作指南,因为看起来每次我写一个受到重创的lambda表达式时我都会浪费一些时间.我必须手动测试它以确保它能够工作,因为我不知道要遵循什么规则.

替代方法

&示例控制台应用程序代码

我还写了一个不同版本的同一个方法,不需要任何变量提升(至少我认为它没有,但你了解这个让我知道是否是这种情况):

public IEnumerable<Item> GetItems(int point)
{
    Func<int, Func<Item, bool>> buildPredicate = p => i => i.IsApplicableFor(p);
    return this.items.Where(buildPredicate(point));
}
Run Code Online (Sandbox Code Playgroud)

在这里查看Gist.只需创建一个控制台应用程序并将整个代码复制到块Program.cs内的文件中namespace.您将看到第二个示例即使不使用自由变量也要慢得多.

一个矛盾的例子

我想构建一些lambda最佳使用指南的原因是我之前遇到过这个问题,令我惊讶的是,当使用谓词构建器 lambda表达式时,结果更快.

现在解释一下.我完全迷失在这里,因为当我知道我的代码中有一些繁重的使用方法时,我可能根本不会使用lambdas.但我想避免这种情况,并深究这一切.

编辑

你的建议似乎不起作用

我已经尝试实现一个自定义查找类,其内部工作方式与编译器使用自由变量lambda类似.但是,我没有使用闭包类,而是实现了模拟类似场景的实例成员.这是代码:

private int Point { get; set; }
private bool IsItemValid(Item item)
{
    return item.IsApplicableFor(this.Point);
}

public IEnumerable<TItem> GetItems(int point)
{
    this.Point = point;
    return this.items.Where(this.IsItemValid);
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,这种方法与慢速版本一样慢.我不知道为什么,但似乎除了速度之外什么也没做.它重用了相同的功能,因为这些附加成员是同一对象实例的一部分.无论如何.我现在非常困惑!

我已经使用这个最新版本更新了Gist源代码,因此您可以自己测试.

Jim*_*hel 3

是什么让您认为第二个版本不需要任何变量提升?您Func使用 Lambda 表达式来定义 ,这将需要与第一个版本所需的相同的编译器技巧。

此外,您正在创建一个Func返回 a 的函数Func,这让我有点费劲,并且几乎肯定需要在每次调用时重新评估。

我建议您在发布模式下编译它,然后使用 ILDASM 检查生成的 IL。这应该能让您深入了解生成的代码。

您应该运行的另一个测试将使您更深入地了解,即使谓词调用成为在类范围内使用变量的单独函数。就像是:

private DateTime dayToCompare;
private bool LocalIsDayWithinRange(TItem i)
{
    return i.IsDayWithinRange(dayToCompare);
}

public override IEnumerable<TItem> GetDayData(DateTime day)
{
    dayToCompare = day;
    return this.items.Where(i => LocalIsDayWithinRange(i));
}
Run Code Online (Sandbox Code Playgroud)

这将告诉您提升day变量是否实际上会让您付出任何代价。

是的,这需要更多代码,我不建议您使用它。正如您在对之前提出类似建议的答案的回复中指出的那样,这将创建相当于使用局部变量的闭包。关键是你或编译器必须做这样的事情才能使事情正常工作。除了编写纯迭代解决方案之外,您无法执行任何魔法来阻止编译器执行此操作。

我的观点是,在我的例子中“创建闭包”是一个简单的变量赋值。如果这比使用 Lambda 表达式的版本快得多,那么您就知道编译器为闭包创建的代码效率低下。

我不确定您从哪里获得有关必须消除自由变量以及关闭成本的信息。你能给我一些参考吗?