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

public IEnumerable<Item> GetItems(int point)
{
    return this.items.Where(i => i.IsApplicableFor(point));
}
那么这里有一些与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));
}
checkout Gist here。只需创建一个控制台应用程序,然后将整个代码复制到Program.cs块内的namespace文件中即可。您将看到第二个示例即使不使用自由变量也要慢得多。
一个矛盾的例子
我之所以要构建一些lambda最佳用法指南,是因为我使用了met this problem before,而令我惊讶的是,当使用谓词生成器lambda表达式时,它的运行速度更快。
现在解释一下。我在这里完全迷路了,因为当我知道我的代码中有一些重用方法时,可能完全不会使用lambda。但我想避免这种情况,并深入了解所有情况。
编辑
您的建议似乎无效
我尝试实现一个自定义查找类,该类在内部工作类似于编译器使用自由变量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);
}
有趣的是,它的运行速度与慢版本一样慢。我不知道为什么,但是似乎除了快速行动外别无其他。它重用了相同的功能,因为这些附加成员是同一对象实例的一部分。反正。 我现在对非常困惑!
我已经用最新的更新更新了Gist source,因此您可以自己进行测试。

最佳答案

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

此外,您正在创建一个Func,它返回Func,这使我的大脑有些弯曲,几乎可以肯定每次调用都需要重新评估。

我建议您在 Release模式下进行编译,然后使用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));
}

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

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

我的意思是,在我的案例中,“创建闭包”是一个简单的变量赋值。如果这比使用Lambda表达式的版本要快得多,则说明编译器为闭包创建的代码效率低下。

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

10-05 20:53
查看更多